I'm new to Symfony and Doctrine and am writing a web app. I added the sfDoctrineGuardPlugin to my project. When I open the schema.yml file for the plugin I see this:
sfGuardUser:
actAs: [Timestampable]
columns:
first_name: string(255)
last_name: string(255)
email_address:
type: string(255)
notnull: true
unique: true
username:
type: string(128)
notnull: true
unique: true
algorithm:
type: string(128)
default: sha1
notnull: true
salt: string(128)
password: string(128)
is_guest:
type: boolean
default: 0
is_active:
type: boolean
default: 1
is_super_admin:
type: boolean
default: false
last_login:
type: timestamp
indexes:
is_active_idx:
fields: [is_active]
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
Does this schema generate a table with a primary key (and if so, how do I access it)? I've looked online and most of the pages that cover the schema for sfGuardUser display an id column that is the primary key. What am I missing? Thanks.
Yes, it's id, accessed as sfGuardUser u --> u.id, as in...
$user = Doctrine::getTable('sfGuardUser')->findOneById(55);
or...
$q = Doctrine_Query::create()
->select('u.*')
->from('sfGuardUser u')
->where('u.id = ?', 55);
$q->execute();
I think somewhere in the Doctrine documentation it says that Doctrine auto-generates an "id" primary key if one isn't declared in the YAML file. It used to be declared explicity in the sfGuardPlugin schema but as of Symfony 1.4.8 (I think), it's just not written.
One thing to watch out for is that elsewhere in your schema, you need to make sure that you declare the same numeric type for the other end of the foreign key relationship or otherwise it'll throw an error. I think it's just type: integer that you need.
If no primary key is given, doctrine will create an id fieldwith type bigint and with a primary key.
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 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 currently using the sfDoctrineGuardPlugin and sfForkedDoctrineApplyPlugin and when I reload data using doctrine:data-load
I get the following:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (my_db.sf_guard_user_profile, CONSTRAINTsf_guard_user_profile_user_id_sf_guard_user_id_1FOREIGN KEY (user_id) REFERENCESsf_guard_user(id) ON DELETE CASCADE)
This is really annoying as I can't seem to load any of the records that are in my fixtures.
I've copied the schema from the sfForkedDoctrineApplyPlugin and extended it a little bit:
sfGuardUserProfile:
actAs:
Timestampable: ~
columns:
user_id:
type: bigint(20)
notnull: true
default:
unsigned: false
primary: false
unique: false
autoincrement: false
email_new:
type: string(255)
unique: true
firstname:
type: string(255)
lastname:
type: string(255)
city:
type: varchar(255)
validate_at:
type: timestamp
validate:
type: string(33)
relations:
User:
class: sfGuardUser
foreign: id
local: user_id
type: one
onDelete: cascade
foreignType: one
foreignAlias: Profile
indexes:
validate:
fields: [validate]
Does anyone know how to fix this?
Thanks
user_id:
type: bigint(20)
ist not compatible to sfGuardUser
sfGuardUser:
type: integer()
In your fixtures for sfGuardUserProfile you need to specify the sfGuardUser link as you have a foreign link to it that cannot be null
sfGuardUserProfile:
bob_profile:
sfGuardUser: bob # Set the link
firstname: bob
# bobs_profile fixtures
sfGuardUser:
bob:
# bobs fixtures
Check User's id type.
It has to be the same as sfGuardUserProfile user_id is: bigint(20)
Ok, so I had to add interger(11) to all of the tables in sfDoctrineGuardPlugin. A bit hacky, but works now
Suppose I have defined VendorClientLicense model like this:
VendorClientLicense:
tableName: vendor_client_licenses
columns:
id:
type: integer(4)
primary: true
notnull: true
autoincrement: true
status:
type: string(255)
default: 'pending'
client_id:
type: integer(8)
notnull: true
vendor_id:
type: integer(8)
notnull: true
relations:
sfGuardUser:
class: sfGuardUser
local: client_id
foreign: id
foreignAlias: VendorClientLicenses
foreignType: many
owningSide: true
sfGuardUser:
class: sfGuardUser
local: vendor_id
foreign: id
foreignAlias: VendorClientLicenses
foreignType: many
owningSide: true
indexes:
fk_vendor_client_licenses_sf_guard_user1:
fields: [client_id]
fk_vendor_client_licenses_sf_guard_user2:
fields: [vendor_id]
options:
charset: utf8
collate: utf8_unicode_ci
If you see the two relations are defined with same name 'sfGuarduser'; What I have found for this in mysql is that in generated database client_id does not show any association with sfGuardUser, whereas vendor_id does! If I change it to 'sfGuardUser1' and 'sfGuardUser2' then shows both relationship! So I assume eventually this has important significance and should not be identical for a same model. Is there any other implications for it?
Plus can you name me a good schema generator like 'mysqlworkbenchdoctrineplugin' which handles situation like this automatically?
Yes, you need to name them differently.
I asked & received for the same problem here:
MySQL: Two foreign keys in one table referring to another table
I haven't had any problems with it.
Regarding the plugin, sorry can't help you.
I'm doing a blog engine using symfony as a learning exercice.
How do I get the list of tags from the id of a blog post ?
Here's the database shema :
I added the following in the model :
public static function getTags($id)
{
return Doctrine_Core::getTable('Tag')
->createQuery('t')
->select('t.name, t.slug')
->leftJoin('t.ContentTag ct')
->where('ct.content_id = ?', $id)
->orderBy('t.name ASC');
}
and here is part of the schema.yml :
Content:
connection: doctrine
tableName: ec_content
actAs:
Sluggable:
fields: [title]
unique: true
canUpdate: true
Timestampable:
columns:
id:
type: integer(4)
fixed: false
unsigned: true
primary: true
autoincrement: true
(...)
relations:
Comment:
local: id
foreign: content_id
type: many
ContentTag:
local: id
foreign: content_id
type: many
ContentTag:
connection: doctrine
tableName: ec_content_tag
columns:
content_id:
type: integer(4)
fixed: false
unsigned: true
primary: true
autoincrement: false
tag_id:
type: integer(4)
fixed: false
unsigned: true
primary: true
autoincrement: false
relations:
Content:
local: content_id
foreign: id
type: one
Tag:
local: tag_id
foreign: id
type: one
It's difficult to tell without seeing exactly how your schema is defined (i.e. schema.yml), but my guess would be that this would work, assuming you have the content object loaded:
$tags = $content->Tags;
Otherwise, your code snippet should work, so far as I can tell. You just need to stick ->exec() on the end to make it return the results of the query rather than the query object itself:
return Doctrine_Core::getTable('Tag')
->createQuery('t')
->select('t.name, t.slug')
->leftJoin('t.ContentTag ct')
->where('ct.content_id = ?', $id)
->orderBy('t.name ASC')
->exec();
Edit Having seen your schema, it seems that you have not created a relationship between Content and Tags, which you need to do. You can let Doctrine handle their interaction. The Symfony and Doctrine book uses something essentially identical to your example to demonstrate how to do a many-to-many relationship. (Note that, although this document is for an out-of-date version of symfony, the syntax for this feature has not changed.)