views:

241

answers:

1

I am getting unexpected behavior (to me!) when defining a one-to-many relationship with Symfony 1.4. Here is a simple example which demonstrates the behavior, having an Employer table and an Employee table: one Employer can have many Employees. The YML schema file is as follows:

Employee:
  columns:
    id: { type: integer, primary: true, autoincrement: true }
    first_name: { type: string(30), notnull: true }
    last_name: { type: string(30), notnull: true }
    employer_id: { type: integer }
  relations:
    Employer:
      local: employer_id
      foreign: id
      type: one
      foreignType: many
      foreignAlias: Workers

Employer:
  columns:
    id: { type: integer, primary: true, autoincrement: true }
    name: { type: string(100) }
    line1: { type: string(100) }
    city: { type: string(100) }
    state: { type: string(10) }

Thus, calling getWorkers() on an Employer should return the Employees associated w/ the Employer. I get this expected behavior when getWorkers() is called the first time.

However, on subsequent calls, if additional employees have been added to the employer (either programmatically or directly in the DB [MySQL]), the getWorkers() call still returns the first results.

I've verified by stepping through the Symfony source for the getWorkers() call that, on the subsequent calls, it's returning the cached value stored in the _references array.

If I follow the foreign key programmatically, querying Employee by employee_id, then I get the full result set.

Can anyone explain this behavior?

Here's some example PHP code that I run in an 'action', which demonstrates the behavior:

// Create common employer.
$employer = new Employer();
$employer->setName("My Employer");
$employer->setLine1("100 Main Street");
$employer->setCity("AnyTown");
$employer->setState("State");
$employer->save();

// Create two employees,
$worker1 = new Employee();
$worker1->setFirstName("John");
$worker1->setLastName("Doe");
$worker1->setEmployer($employer);
$worker1->save();

$worker2 = new Employee();
$worker2->setFirstName("Jane");
$worker2->setLastName("Smith");
$worker2->setEmployer($employer);
$worker2->save();

$myEmployer = Doctrine_Core::getTable('Employer')->findOneBy('name', 'My Employer');
$workers = $myEmployer->getWorkers();
echo "Number of workers for " . $myEmployer->getName() . ' is ' . count($workers);
// This gives the expected 2 employees.

// Now create another employee in the common employer.
$worker3 = new Employee();
$worker3->setFirstName("Anne");
$worker3->setLastName("Droid");
$worker3->setEmployer($employer);
$worker3->save();

$myEmployer = Doctrine_Core::getTable('Employer')->findOneBy('name', 'My Employer');
$workers = $myEmployer->getWorkers();
echo "Number of workers for " . $myEmployer->getName() . ' is ' . count($workers);
// This still gives 2 employees, whereas there are 3 in the DB.

// Follow FK directly.
$workers = Doctrine_Core::getTable('Employee')->findBy('employer_id', $myEmployer->getId());
echo "Number of workers for " . $myEmployer->getName() . ' is ' . count($workers);
// This gives the expected 3 employees.

How do I force it to re-follow the relation every time?

+2  A: 

When adding new related records, you may need to refresh the relationships of your records.

You can do either of those :

$worker->refresh(true);
$worker->refreshRelated();
$worker->refreshRelated('Employer');

For more infos you should check this : Doctrine Documentation - Refreshing Relationships

DuoSRX
Thanks very much. Works great! Is it common in ORMs to have to make the explicit call to sync objects with the DB? My initial reaction is that I would want the option/default to always fetch from/sync with the DB unless I tell it otherwise. If not, you have to remember to insert the 'refresh' calls every time you think some code/process might have updated the associated data. I can see some value in doing it after an explicit save() call, but what about data that's also being populated independently of the ORM framework, via an external feed, for example?
Litotes