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.
Related
I've inherited the development of a symfony 1.4 app (though It's my first symfony project).
After digging a bit on that app I've discovered a bug that is driving me nuts.
On the admin generator list page of a module there are two select fields in the filter form that should list each one a type of users (partners and clients), however both listings show all the users.
The generator.yml shows that both select fields should be populated by parter_id and client_id
[...]
filter:
display: [date, client_id, partner_id, ...]
form: ~
edit: ~
new: ~
[...]
Looking at the schema of the module it's obvious why symfony is populating the select fields with the same content because the relations of both client_id and partner_id are equivalent:
SomeModule:
tableName: client_requests
columns:
id: {type: integer, primary: true, autoincrement: true}
partner_id: {type: integer, notnull: true}
client_id: {type: integer, notnull: true}
date: {type: timestamp}
...
relations:
partner:
class: sfGuardUser
local: partner_id
foreign: id
type: one
client:
class: sfGuardUser
local: client_id
foreign: id
type: one
As per the above schema symfony has no other choice but to generate the select fields as:
select * from sf_guard_user;
The difference between partner and client is made by sfGuardGroup and sfGuardUserGroup, the related parts of the schema from ./plugins/sfDoctrineGuardPlugin/config/doctrine/schema.yml are:
sfGuardGroup:
actAs: [Timestampable]
columns:
name:
type: string(255)
unique: true
description: string(1000)
relations:
Users:
class: sfGuardUser
refClass: sfGuardUserGroup
local: group_id
foreign: user_id
foreignAlias: Groups
...
sfGuardUser:
actAs: [Timestampable]
columns:
first_name: string(255)
last_name: string(255
...
relations:
Groups:
class: sfGuardGroup
local: user_id
foreign: group_id
refClass: sfGuardUserGroup
foreignAlias: Users
Permissions:
class: sfGuardPermission
local: user_id
foreign: permission_id
refClass: sfGuardUserPermission
foreignAlias: Users
...
What I would like to is to generate those fields with a query like:
select u.*
from sf_guard_user as u
JOIN (sf_guard_user_group as ug JOIN sf_guard_group as g
ON (ug.group_id = g.id)) ON (u.id = ug.user_id)
where g.name = 'partner';
I've researched for a way to edit the module's schema to reflect the relation between client_id and partner_id with their group name to get the filter form select fields right, but I've ended believing that that is a wrong approach
¿is it possible to modify the module's schema to allow symfony to populate correctly the select fields?
¿or that approach is completely wrong and instead I should hack the methods that render those fields to populate them with the right queries? If so hints in that direction would be appreciated.
Thanks
Just edit your form (probably sfGuardUserForm) and add the query to the partner_id widget. Something like:
$query = Doctrine::getTable('sfGuardUser')->createQuery('u')->etc etc...
$this->widgetSchema['partner_id']->setOption('query', $query)
You'll probably need to do the same to the validator for that field:
$this->validatorSchema['partner_id']->setOption('query', $query)
If you cannot edit that specific form (maybe it is used in another part of the app), just make a new one that extends it. And then, you just need to specify the form class in the generator.yml file.
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.
I want to make private messages in my site. I want that user can delete received or sent messages. For it I need two tables one for send message and one for receive....Is it to possible that when user send messages, it automatically add fro two tables? or it is better to make some relations with tables? or maybe exist better solution?
I think you need just one table:
Message:
columns:
from: integer
to: integer
header: string(100)
body: blob
show_in_outcoming:
type: boolean
default: true
show_in_incoming:
type: boolean
default: true
is_read:
type: boolean
default: false
UserFrom:
class: sfGuardUser
local: from
foreign: id
foreignType: one
type: one
UserTo:
class: sfGuardUser
local: to
foreign: id
foreignType: one
type: one
where
is_read indicates whether or not the message been read (false - unread, true - read - for incoming messages only)
If the user who sends the message want to delete it, we simply hide it (not delete from the DB) - set show_in_outcoming to false.
If the user who received the message want to delete it, we hide it too - set show_in_incoming to false. This approach allows us to recover the "hidden" messages (or remove them altogether)
Maybe, you should create a message table, with tow relation to user table:
message:
columns:
user_emitter_id: ...
user_reciever_id: ...
body: ...
relations:
userEmitter:
class: user
local: user_emitter_id
foreign: id
userReciever:
class: user
local: user_reciever_id
foreign: id
There is a symfony plugin that allows you to do this. Check sfSocialPlugin.
Message:
columns:
from: integer
to: integer
header: string(100)
body: blob
show_in_outcoming:
type: boolean
default: true
show_in_incoming:
type: boolean
default: true
is_read:
type: boolean
default: false
relations:
UserFrom:
class: sfGuardUser
local: from
foreign: id
foreignType: one
type: one
UserTo:
class: sfGuardUser
local: to
foreign: id
foreignType: one
type: one
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 doing a blog engine in Symfony. Here's part of my schema :
Content:
connection: doctrine
tableName: ec_content
columns:
id:
type: integer(4)
fixed: false
unsigned: true
primary: true
autoincrement: true
(...)
relations:
Author:
local: author_id
foreign: id
type: one
State:
local: state_id
foreign: id
type: one
Type:
local: type_id
foreign: id
type: one
(...)
In the administration pages, I want to display the type of the articles, but symfony only shows the type_id, why is that ?
EDIT: here's my generator.yml : I haven't modified it much yet.
generator:
class: sfDoctrineGenerator
param:
model_class: Content
theme: admin
non_verbose_templates: true
with_show: false
singular: ~
plural: ~
route_prefix: content_Brouillons
with_doctrine_route: true
actions_base_class: sfActions
config:
actions: ~
fields: ~
list:
title: Brouillons
display: [Type, State, title, link]
filter: ~
form: ~
edit: ~
new: ~
OK.
In your generator.yml, on the display line, Symfony (via Doctrine) will look for a field name in your model class that corresponds to each field you want to display. If the field name doesn't exist, it will then look for a corresponding getFieldName() method and call that.
In your example, you have Type as a field name, which will be calling getType() - this will fetch the relation in. By default, Doctrine assumes that when you want to convert a model to a string (eg for display in your list), you want to use the primary key - in your case, the ID value.
To overcome this, add a __toString() method as follows, to your Doctrine lib/model/doctrine/EcType.class.php file:
class EcType extends BaseEcType
{
public function __toString()
{
return $this->type;
}
}
and you should see the 'type' field being displayed in your admin generated list.