Categories
Phalcon Survival Guide for Developers

Two Annoying Phalcon Errors and How to Fix Them: e_id and SoftDelete

If you’re using Phalcon, you’ve probably fallen in love with its speed and flexibility. But let’s be real—sometimes it throws curveballs that make you question your life choices.

Problem: What’s Going Wrong with Phalcon?

I ran into two particularly frustrating issues:

  1. The e_id Error: You write a query with a column like note_id, and Phalcon freaks out with a “Column ‘e_id’ doesn’t exist” error.
  2. SoftDelete Hard Delete Woes: You’re using the SoftDelete behavior, but sometimes you need to permanently delete records. Phalcon’s model initialization and caching make this trickier than it should be.

Sound familiar? Let’s dive into why these issues are such a pain and how to fix them.

Agitate: Why These Problems Drive You Nuts

The e_id Error

Imagine you’re querying your NotesTranslations model like this:

$translation = NotesTranslations::findFirst([
    'conditions' => 'note_id = :noteId:',
    'bind' => ['noteId' => 123]
]);

And then, BOOM! You get this error:
Column 'e_id' doesn't belong to any of the selected models.
What’s going on? Phalcon’s PHQL engine mistakenly parses note_id as not e_id, treating not as an operator and e_id as a column. Spoiler alert: there’s no e_id in your database! You spend hours digging through stack traces, searching forums, and pulling your hair out because the solution is nowhere to be found. This error pops up whenever a column name contains something like not, and it’s a total productivity killer.

The SoftDelete Problem

With SoftDelete, you mark records as deleted (e.g., setting is_deleted = 1) instead of removing them. But what if you need to actually delete a record—say, for a test environment or legal reasons? Phalcon’s modelsManager initializes models and caches their metadata on the first query, so disabling SoftDelete at runtime is nearly impossible. I tried everything:

  • Adding a static $disableSoftDelete flag? Nope, the model’s already cached.
  • Creating a child class with an empty initialize? Phalcon still uses the parent class.
  • Writing raw PHQL queries? The model’s behaviors still kick in.

It’s like Phalcon is saying, “You chose soft delete, now live with it!” Meanwhile, your project deadlines are looming.

Solution: Here’s How to Fix It

1. Fixing the e_id Error

The fix for the e_id error is simple but clever: wrap column names in square brackets ([]) in PHQL queries. This tells Phalcon to treat the column name as a literal string. Here’s how:

$translation = NotesTranslations::findFirst([
    'conditions' => '[note_id] = :noteId: AND [language_id] = :languageId:',
    'bind' => [
        'noteId' => 123,
        'languageId' => 1
    ]
]);

This tiny change stops Phalcon from misinterpreting note_id as not e_id. No more errors, and your query works like a charm! Note: This only applies to PHQL queries. If you’re using raw SQL with PDO, you don’t need the brackets.

Pro Tip: If your column names include other tricky words like order or set, always use [column_name] in PHQL to avoid similar issues.

2. Bypassing SoftDelete for Permanent Deletion

To permanently delete records while using SoftDelete, the most reliable solution is to bypass Phalcon’s model layer entirely and use PDO with raw SQL. Why? Because PHQL queries trigger model initialization, which brings SoftDelete back into play. Here’s how to do it:

Step 1: Raw SQL with PDO

Add a deletePermanently method to your Notes model:

public function deletePermanently()
{
    $messages = [];
    $db = $this->getDI()->getDb(); // Get PDO connection

    try {

        $sql = "DELETE FROM notes WHERE id = :id";
        $stmt = $db->prepare($sql);
        $stmt->execute(['id' => $this->id]);
        error_log("Deleted {$stmt->rowCount()} rows from notes for id = {$this->id}");

        return [true, $messages];
    } catch (\PDOException $e) {
        $messages[] = "Database error: {$e->getMessage()}";
        error_log("Error in deletePermanently: {$e->getMessage()}");
        return [false, $messages];
    }
}

Step 2: Use It in the Controller

Call this method in your NotesController:

public function deletePermanentlyAction()
{
    $json = $this->request->getJsonRawBody();

    if (!isset($json->noteId)) {
        return $this->response->setJsonContent([
            'status' => 'error',
            'success' => false,
            'error' => 'Note ID is required',
            'data' => []
        ]);
    }

    $note = Notes::findFirst([
        'conditions' => '[id] = :id:',
        'bind' => ['id' => (int)$json->noteId]
    ]);

    if (!$note) {
        return $this->response->setJsonContent([
            'status' => 'error',
            'success' => false,
            'error' => 'Note not found',
            'data' => []
        ]);
    }

    try {
        [$success, $messages] = $note->deletePermanently();
        if (!$success) {
            return $this->response->setJsonContent([
                'status' => 'error',
                'success' => false,
                'error' => $messages ?: ['Failed to delete permanently'],
                'data' => []
            ]);
        }
    } catch (\Exception $e) {
        return $this->response->setJsonContent([
            'status' => 'error',
            'success' => false,
            'error' => $e->getMessage(),
            'data' => []
        ]);
    }

    return $this->response->setJsonContent([
        'status' => 'success',
        'success' => true,
        'error' => null,
        'data' => []
    ]);
}

Step 3: Debug Like a Pro

If the deletion fails, check for database constraints:

SHOW CREATE TABLE notes_translations;

If there’s a foreign key without ON DELETE CASCADE, the code above handles it by deleting related records first.

Pro Tip: Log the number of affected rows ($stmt->rowCount()) to confirm which tables are being modified. This makes debugging a breeze.

Final Thoughts

Phalcon is a beast of a framework, but these quirks can trip up even seasoned developers. Fixing the e_id error with [note_id] and bypassing SoftDelete with PDO will save you hours of frustration. Hopefully, these solutions make your Phalcon projects smoother! If you hit another Phalcon roadblock, check my blog or drop me a line—we’ll figure it out together!

Leave a Reply

Your email address will not be published. Required fields are marked *