Many to many relation on same table - symfony1

I've got a little problem with many to many relations on the same table using Symfony 1.4 with the Propel ORM. My schema.yml looks like this:
propel:
item:
_attributes: { phpName: Item }
id: { phpName: Id, type: INTEGER, size: '10', primaryKey: true, autoIncrement: true, required: true }
type_id: { phpName: TypeId, type: INTEGER, size: '11', required: true, foreignTable: type, foreignReference: id, onDelete: RESTRICT, onUpdate: RESTRICT }
owner_id: { phpName: OwnerId, type: INTEGER, size: '11', required: true, foreignTable: owner, foreignReference: id, onDelete: RESTRICT, onUpdate: RESTRICT }
place_id: { phpName: PlaceId, type: INTEGER, size: '11', required: true, foreignTable: place, foreignReference: id, onDelete: RESTRICT, onUpdate: RESTRICT }
picture_id: { phpName: PictureId, type: INTEGER, size: '11', required: false, foreignTable: picture, foreignReference: id, onDelete: RESTRICT, onUpdate: RESTRICT }
supplier_id: { phpName: SupplierId, type: INTEGER, size: '11', required: false, foreignTable: supplier, foreignReference: id, onDelete: RESTRICT, onUpdate: RESTRICT }
name: { phpName: Name, type: VARCHAR, size: '255', required: true }
amount: { phpName: Amount, type: INTEGER, size: '11', required: true }
wished_amount: { phpName: WishedAmount, type: INTEGER, size: '11', required: true }
costs: { phpName: Costs, type: DECIMAL, size: '7', scale: '2', required: true }
description: { phpName: Description, type: LONGVARCHAR, required: false }
use_until: { phpName: UseUntil, type: DATE, required: false }
last_used: { phpName: LastUsed, type: TIMESTAMP, required: false }
last_updated: { phpName: LastUpdated, type: TIMESTAMP, required: false }
_indexes: { item_FI_1: [type_id], item_FI_2: [owner_id], item_FI_3: [place_id], item_FI_4: [picture_id], item_FI_5: [supplier_id] }
item_has_item:
item_id: { type: INTEGER, required: true, foreignTable: item, foreignAlias: item, foreignReference: id, primaryKey: true}
parent_item_id: { type: INTEGER, required: true, foreignTable: item, foreignAlias: parent_item, foreignReference: id, primaryKey: true}
_indexes: { item_has_item_FKIndex1: [item_id], item_has_item_FKIndex2: [parent_item_id] }
However, the needed admin_double_list in the admin generator does not appear automatically. It does, when the second foreignTable is adjusted to another table, for example sf_guard_user. I've read that this could be fixed in Propel 1.3, but I don't know which Propel version is included with Symfony 1.4.
I hope I gave enough information for my problem to be solved by you guys. Otherwise I will have to make another table which holds the items that have child item, like this:
Item <---- Item_has_item <---- Combined_item
EDIT AFTER REACTION FROM j0k
After the reaction from j0k, I did update the sfPropelORMPlugin, and it did recognise the relation. However, I still got an error while saving (only when a relation is made):
Unable to execute INSERT statement [INSERT INTO `item_has_item` (`ITEM_ID`) VALUES (:p0)]
[wrapped: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a
child row: a foreign key constraint fails (`SI/item_has_item`, CONSTRAINT
`item_has_item_FK_1` FOREIGN KEY (`parent_item_id`) REFERENCES `item` (`id`))]
I have found, after a long extensive search, that the form generator fails to correctly understand the many-to-many relation on the same table. In the function saveItemHasItemList($con = null):
$c = new Criteria();
$c->add(ItemHasItemPeer::ITEM_ID, $this->object->getPrimaryKey());
ItemHasItemPeer::doDelete($c, $con);
$values = $this->getValue('item_has_item_list');
if (is_array($values))
{
foreach ($values as $value)
{
$obj = new ItemHasItem();
$obj->setItemId($this->object->getPrimaryKey());
$obj->setItemId($value);
$obj->save();
}
}
As you can see, the ItemId is twice set, while the ParentItemId is not set, hence the constraint value. It should be generated as:
$c->add(ItemHasItemPeer::ITEM_ID, $this->object->getPrimaryKey());
$c->add(ItemHasItemPeer::PARENT_ITEM_ID, $this->object->getPrimaryKey());
&&
$obj->setParentItemId($this->object->getPrimaryKey());
$obj->setItemId($value);
This solves the saving problem, however, it still does not show the already selected relations. To solve that, you should also change the updateDefaultsFromObject():
foreach ($this->object->getItemHasItemsRelatedByItemId() as $obj)
Should be:
foreach ($this->object->getItemHasItemsRelatedByParentItemId() as $obj)
Remember, these functions are located in form/base/BaseItemForm.class.php, but the functions should be overwritten in /form/ItemForm.class.php.
I believe this is an Propel bug, so I will post it tomorrow on the Propel TRAC.

If you are using the last symfony 1.4, it uses Propel 1.4.2.
You should try to use a new version of the Propel Plugin. It uses Propel 1.6.

Related

Overriding Symfony Form's Save Function

I have two issues, need some help with.
I have a table which is referenced by a foreign key to a second table:
member_child:
_attributes: { phpName: MemberChild }
id: { type: INTEGER, size: '11', primaryKey: true, autoIncrement: true, required: true }
member_id: { type: INTEGER, size: '11', required: true, foreignTable: member, foreignReference: id }
child_id: { type: INTEGER, size: '11', required: true, foreignTable: child, foreignReference: id }
and child:
child:
_attributes: { phpName: Child }
id: { type: INTEGER, size: '11', primaryKey: true, autoIncrement: true, required: true, onDelete: cascade }
username: { type: VARCHAR, size: '45', required: true, defaultValue: '' }
display: { type: TINYINT, size: '1', required: true, defaultValue: '1' }
...etc
(obviously this is propel)
Now, when I want to create a child object, using a form, I need to do two things:
On submit, submit a member id
override the doSave function so when the child is created, I can also create the member_child object
How can I accomplish these issues?
I agree, you can use embedForm like pankar said. Also you can override save method of your forms like this:
$this->childForm = new ChildForm();
$this->childMemberForm = new ChildMemberForm();
//binding, checking if form was sent etc.
if ($this->childForm->isValid() && $this->childMemberForm->isValid())
{
//save method should return saved object
$childObject = $this->childForm->save();
//therefore this id could be used by next object
$this->childMemberForm->save(childObject->getId());
}
I hope that will help you!
You can always use the built-in Symfony feature sfForm::embedForm in your parent form in order to save the child one, but I haven't figure out a a way of properly get this working.
One post I came across some time ago actually did provide me with the solution. Have a look and see if it fits your needs. Of course it's in Doctrine but I suppose it can be easily ported in Propel

How to prevent Symfony to add auto increment field? (propel)

there is a simple definition:
notifications:
_attributes: { phpName: Notifications, noXsd: false, defaultIdMethod: none, package: lib.model }
auction_id: { type: integer, size: '10', primarykey: true, required: true, defaultvalue: '0', foreignTable: auctions, foreignReference: id, onDelete: cascade, onUpdate: cascade }
alertwhenitsover: { type: tinyint, size: '1', required: true, defaultvalue: '0' }
but it always adds auto increment field... darn I dont want it! How to tell to symfony not to add it?

symfony - embeddedForm - multiple checkboxes

I have article admin module and a tag module
Tags are simply a single tag per row item
What I'd like to do is to embed the list of all the tags (as checkboxes) into my article module
Could I do this with embedded forms?
EDIT:
This is my schema:
article:
id: ~
title: { type: VARCHAR, size: '255', required: true }
tags: { type: VARCHAR, size: '500' }
created_at: { type: TIMESTAMP, required: true }
updated_at: { type: TIMESTAMP, required: true }
tag:
id: ~
tag: { type: VARCHAR, size: '500', required: true }
ord_id: { type: INTEGER, required: true }
created_at: ~
updated_at: ~
item_tag:
id: ~
item_id: { type: INTEGER, required: true, foreignTable: item, foreignReference: id, onDelete: cascade }
tag_id: { type: INTEGER, required: true, foreignTable: tag, foreignReference: id, onDelete: restrict }
created_at: ~
item:
id: ~
article_id: { type: INTEGER, foreignTable: article, foreignReference: id, onDelete: cascade }
So when I need the tags to be displayed and will update the above tables
If you have defined the relationship between the article and tags correctly in your model, then the generated Forms should contain tag select widgets.
Search "sfWidgetFormChoice" in the Forms documentation for more information:
http://www.symfony-project.org/jobeet/1_4/Doctrine/en/10
Note: The examples are created using the Doctrine ORM but everything should work the same way with Propel as well.

Relation one to one in sf1.4/propel

i have installed sfGuardPlugin and created this model:
propel:
sf_guard_user_profile:
_attributes: { phpName: sfGuardUserProfile }
id: ~
user_id: { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: true, onDelete: cascade }
name: varchar(50)
As it is written here, http://www.propelorm.org/wiki/Documentation/1.4/Relationships (see "One-to-one relationships"), It is supposed symfony generates the function sfGuardUser->getSfGuardUserProfile() and sfGuardUserProfile->getSfGuardUser() but I have this code:
// this works
$c1 = new Criteria();
$elements = sfGuardUserProfilePeer::doSelect($c1);
var_dump($elements[0]->getSfGuardUser());
// this doesn't work
$c2 = new Criteria();
$elements = sfGuardUserPeer::doSelect($c2);
var_dump($elements[0]->getSfGuardUserProfile());
and it doesn't work. It says:
Call to undefined method
BasesfGuardUser::getSfGuardUserProfile
sf 1.4/propel 1.4
Javier
the user_id field in the sfGuardProfile should be a primary key, so propel will see it as a one-to-one relation.
propel:
sf_guard_user_profile:
_attributes: { phpName: sfGuardUserProfile }
user_id: { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: true, onDelete: cascade, primary: true }
name: varchar(50)
as your sfGuardUserProfile is a one-to-many relation with your sfGuardUser, so the getSfGuardUserProfile() method doesn't exist, the method that does exist is sfGuardUserProfiles() (the only difference is an 's' in the method name, and it will result an array of user profile)
ps: sorry for my bad english :D

Does embedRelation() (sfPropel15Plugin) works for many-to-many relationships?

does embedRelation() (sfPropel15Plugin) works for many-to-many
relationships? If not any experience or tutorial to get that (embed
forms for m:m relations)?
Regards
Javi
schema.yml
product:
id: ~
name: {type: varchar(255), required: true}
price: {type: integer, required: true}
order_list:
id: ~
order_id: {type: integer, required: true, foreignTable: order, foreignReference: id, onDelete: cascade, onUpdate: cascade}
product_id: {type: integer, required: true, foreignTable: product, foreignReference: id, onDelete: cascade, onUpdate: cascade}
order:
id: ~
name: {type: varchar(255), required: true}
Class form:
class OrderForm extends BaseOrderForm
{
public function configure()
{
$this->embedRelation('OrderList');
}
}
Add it look like:

Resources