I have changed the schema of a Symfony application by transforming a one-to-many relationship to a many-to-many relationship between a Sample model and a Collector model. The Sample model had a collector_id foreign key and now there is an intermediate model (SampleCollectors) with two foreign keys: sample_id and collector_id.
Sample:
columns:
id: { type: integer, primary: true, autoincrement: true }
...
collector_id: ... # This column is removed
...
relations:
...
Collectors: { foreignAlias: Collectors, class: Collector, local: sample_id, foreign: collector_id, refClass: SampleCollectors }
Collector:
columns:
id: { type: integer, primary: true, autoincrement: true }
...
relations:
Samples: { foreignAlias: Samples, class: Sample, local: collector_id, foreign: sample_id, refClass: SampleCollectors }
SampleCollectors:
columns:
sample_id: { type: integer, primary: true }
collector_id: { type: integer, primary: true }
relations:
Sample: { onDelete: cascade }
I have edited the schema.yml file like above and executed the following tasks:
php symfony doc:generate-migrations-diff
php symfony doc:build --all-classes
which has rebuilt every involved model/form/filter and created two migration classes.
Since the database is already in production-mode, I have tuned the migration classes to move the foreign key of the one-to-many relationship (Sample.collector_id) to the many-to-many intermediate table (SampleCollectors.collector_id):
public function preUp() {
$sampleTable = Doctrine_Core::getTable('Sample');
// I do this because collector_id has disappeared after model regeneration.
$sampleTable->setColumn('collector_id', 'integer', null, array('type' => 'integer'));
$this->samples = $sampleTable->findAll()->toArray();
}
public function up() {
// ...
$this->removeColumn('sample', 'collector_id');
// ...
$this->createTable('sample_collectors', array(...), array(
'type' => 'INNODB',
'primary' => array(0 => 'sample_id', 1 => 'collector_id'),
...));
}
public function postUp() {
foreach ( $this->samples as $sample ) {
$sampleCollector = new SampleCollectors();
$sampleCollector->setSampleId($sample['id']);
$sampleCollector->setCollectorId($sample['collector_id']);
$sampleCollector->trySave();
}
}
I have migrated the database and everything went apparently well.
But now that I'm updating the controllers, views, etc., Doctrine still tries to retrieve the collector_id column from Sample, instead of navigating through the many-to-many relationship.
Since the Sample model does not store a reference to it anymore, why does Doctrine insist on that? I checked the BaseSample class and it does not have a hasColumn() method setting a column collector_id. BTW, I clear the cache as well.
Thanks!
I finally found the problem. I had configured Doctrine to use the APC cache system in ProjectConfiguration class:
public function configureDoctrine(Doctrine_Manager $manager) {
$manager->setAttribute(Doctrine::ATTR_QUERY_CACHE, new Doctrine_Cache_Apc());
}
This cache is not cleared when you execute php symfony cache:clear, since is not a Symfony thing but a PHP and web server thing.
You can either restart the web server or use apc_clear_cache(), as noted in this question regarding APC cache.
Related
I need some help here understanding how this works. See I'm using sfDoctrineGuardPlugin and add a Profile table as follow:
SfGuardUserProfile:
connection: doctrine
tableName: sf_guard_user_profile
columns:
id: { type: integer(8), primary: true }
user_id: { type: integer(8), primary: false }
idempresa: { type: integer(4), primary: false }
relations:
User:
local: user_id
class: sfGuardUser
type: one
foreignType: one
foreignAlias: SfGuardUserProfile
onDelete: CASCADE
onUpdate: CASCADE
Now I need to setup a field value based on logged in user in SdrivingEmisorForm, for that I need to access to idempresa on sf_guard_user_profile but I don`t know how :-( I tried all this:
$user = sfContext::getInstance()->getUser();
echo $user->getGuardUser()->getProfile()->getIdempresa();
echo $this->getUser()->getGuardUser()->getProfile()->getIdempresa();
echo $user->getProfile()->getIdempresa();
and none works, which is the right way to access to profile fields based on my schema definition? Can any take a brief and explain a bit how I must understand this in order to no get the same doubt if things changes some day?
EDIT
I've found the solution but using sfContext::getInstance() which many says is wrong, so in this case what is the right way to do this. Below th code works for me:
$user = sfContext::getInstance()->getUser()->getGuardUser()->getSfGuardUserProfile()->getIdempresa();
PS: I'm access from SdrivingEmisorForm class
You are defining the foreignAlias as SfGuardUserProfile, so you should use this name for the getter.
In some action:
$profile = $this->getUser()->getGuardUser()->getSfGuardUserProfile();
In some template:
$profile = $sf_user->getGuardUser()->getSfGuardUserProfile();
Then:
echo $profile->getIdempresa();
I have simple schema with one-to-one relation:
Site:
columns:
name: { type: string(255) }
User:
columns:
name: { type: string(255) }
site_id: { type: integer }
relations:
Site: { foreignType: one }
I am overriding Site::save() method (which is called when I edit Site from admin generator), where I need to do smth with User, and User references Site again:
echo $this->getUser()->getSite()->getName();
When getSite() is called, $this is overwritten by values from database, because Doctrine loads "Site" relation from db and replaces $this according to identity map. Obviously, all changes to object are lost. After some code reading i got that User object does not have _references['Site'] set after getUser(), that's why Doctrine tries to load Site from db. If i prepend
$this->getUser()->setSite($this);
, then everything works as expected and no additional queries are made. Another workaround is to join relations in query:
$this->site = Doctrine::getTable('Site')
->createQuery('s')
->leftJoin('s.User u')
->leftJoin('u.Site us')
->fetchOne();
But is that an intended behaviour? How can I make Doctrine know that $this === $this->getUser()->getSite()? I use symfony 1.4.18 with Doctrine 1.2.4. Thank you.
I'm using symfony for a project but got stuck at one point building the schema. In the schema I have one table user that can be created by himself or and administrator and the user ids are stored in sfGuardUser... how can y make a relation between them in the schema.yml?
I have this fields:
tableX
sf_guard_user_id: {type: integer}
sf_guard_user_id_cs: {type: integer}
The first id is the one he gets when created and the second is the administrator's id if it was created by one. So basically, I'm gonna use the id field from sfGuardUser twice in tableX but I'm not able to make this two relations in the schema.yml. Anyone knows how to do the relations?
It is simple. For example:
CoolTable:
actAs:
Timestampable: ~
columns:
sf_guard_user_id: { type: integer() }
sf_guard_user_id_cs: { type: integer() }
relations:
User: { local: sf_guard_user_id, foreign: id, class: sfGuardUser }
UserCs: { local: sf_guard_user_id_cs, foreign: id, class: sfGuardUser }
Main point, that you specify the class of relations.
I am creating a simple CMS and I have a set of templates, each template can have multiple blocks within them.
So I have a one-to-many relationship between the templates and blocks (1 template can have many blocks)
So, when I create a block, it has a drop down of the templates that I can associate the block to using sfDoctrineChoice widget.
In my BlockForm.class.php
new sfWidgetFormDoctrineChoice(array('model' => 'Template', 'multiple'=>true, 'expanded'=>false))
My schema is:
Template:
actAs:
Timestampable: ~
columns:
name:
type: varchar(255)
layout:
type: text
relations:
Block:
class: Block
local: id
foreign: template_id
type: many
foreignType: one
alias: Block
foreignAlias: Template
Block:
columns:
template_id: { type: integer(8), notnull: true }
content: { type: clob, notnull: true }
The problem comes when I try to save the choices. It gives me a:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens when I select 2 or more templates or when I select 1 option:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (db.block, CONSTRAINTblock_template_id_template_idFOREIGN KEY (template_id) REFERENCEStemplate(id))
Is my schema correct to do what I'm looking to do?
Thanks
Build the database tables the way you want them first.
Rename your old schema.yml to schema-20110610.yml
Run task symfony doctrine:build-schema
Take a look at the new schema.yml file you created. It may give you an idea of what you are doing wrong...
You must add foreign key constraint. onDelete and onUpdate.
Template:
actAs:
Timestampable: ~
columns:
name:
type: varchar(255)
layout:
type: text
Block:
columns:
template_id: { type: integer(8), notnull: true }
content: { type: clob, notnull: true }
relations:
Template:
local: template_id
foreign: id
foreignAlias: Blocks
I'm using Symfony 1.4 with Doctrine.
Here's my initial schema:
Page:
tableName: page
columns:
ref:
type: string(50)
notnull: true
unique: true
I'd like to remove the index on the ref column using migrations.
So the schema becomes:
Page:
tableName: page
columns:
ref:
type: string(50)
notnull: true
And my migration file is something like:
class Changepageref extends Doctrine_Migration_Base
{
public function up()
{
$this->removeIndex('page','ref');
}
public function down()
{
$this->addIndex('page','ref', array('fields'=>array('ref'=>array()),'unique'=>true));
}
}
But this won't work when I run it because it's looking for an index named "ref_idx". But if I look at my database, doctrine created an index named "ref", not "ref_idx".
What am I doing wrong?
You can define the default naming of the indexes: the default is "_idx". You can change in on the connection manager.
http://www.doctrine-project.org/projects/orm/1.2/docs/manual/configuration/en#naming-convention-attributes shows it like
$manager->setAttribute(Doctrine_Core::ATTR_IDXNAME_FORMAT,
'%s_index');
Try to set it there and it should be persistent throughout your application with the index names.