Overriding Symfony Form's Save Function - symfony1

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

Related

Many to many relation on same table

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.

Propel archivable behavior doesn't work properly (archived_at column doesn't get updated on insert)

I have some problems with the Propel's archivable behavior. For some reasons, Propel doesn't set archived_at field to the current datetime when the object is being archived.
My schema:
SeminarCustomer:
tableName: seminar_customer
columns:
id: { type: integer, required: true, primaryKey: true, foreignClass: Customer, foreignReference: id, onDelete: cascade }
...
office_id: { type: integer, required: false, foreignTable: office, foreignReference: id }
entity_id: { type: integer, required: true, default: 1 }
propel_behaviors:
timestampable: ~
archivable: ~
SeminarCustomer::archive method:
public function archive(PropelPDO $con = null)
{
if ($this->isNew()) {
throw new PropelException('New objects cannot be archived. You must save the current object before calling archive().');
}
if (!$archive = $this->getArchive($con)) {
$archive = new SeminarCustomerArchive();
$archive->setPrimaryKey($this->getPrimaryKey());
}
$this->copyInto($archive, $deepCopy = false, $makeNew = false);
// NOTE: here should be $archive->setArchivedAt(time());
$archive->save($con);
return $archive;
}
archived_at column definitely exists in my seminar_customer_archive table.
Does anybody know what I am doing wrong ? Or maybe there is an error in the schema?
Symfony: 1.4.17-DEV
Propel: 1.6.3
Thanks in advance!
can you force the log_archived_at parameter of the Archivable behavior? It should work out of the box...
If it can helps, here is the documentation: http://www.propelorm.org/behaviors/archivable.html
EDIT: Fixed by PR #310

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.

An example on how to embed forms in Symfony

I am using Symfony 1.3.2 with Propel ORM on Ubuntu 9.10.
I have a user profile table, which has many other tables linked to it (i.e. user_profile.id is a FK in many other tables.
My db schema looks something like this:
user_profile:
_attributes: { phpName: UserProfile }
id: ~
guard_id: { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: true }
address: { type: longvarchar, required: true }
vehicle_type:
_attributes: { phpName: VehicleType }
id: ~
name: { type: varchar(32), required: true }
user_vehicle:
_attributes: { phpName: UserVehicle }
id: ~
user_id: { type: integer, foreignTable: user_profile, foreignReference: id, required: true }
vehicle_type: { type: integer, foreignTable: vehicle_type, foreignReference: id, required: true }
license_plate: { type: varchar(16), required: true }
user_child:
_attributes: { phpName: UserChild }
id: ~
user_id: { type: integer, foreignTable: user_profile, foreignReference: id, required: true }
gender: { type: boolean, required: true }
name: { type: varchar(32), required: true }
I would like to embed the other objects that link to the user profile object, in the user profile form, so that when I am performing CRUD on a user profile form, the related objects (e.g. UserVehicle, UserJob are also CRUD at the same time as the user profile object).
I need a simple snippet that will show how to:
Embed the various related objects (i.e. UserVehicle, UserChild) into the UserProfile form
Create/Update/Delete the various related objects as the operation is being carried (please note, a user can have more than 0-N vehicles or children assigned to them
Have you read the documentation?:
// lib/form/doctrine/ProductForm.class.php
public function configure()
{
$subForm = new sfForm();
for ($i = 0; $i < 2; $i++)
{
$productPhoto = new ProductPhoto();
$productPhoto->Product = $this->getObject();
$form = new ProductPhotoForm($productPhoto);
$subForm->embedForm($i, $form);
}
$this->embedForm('newPhotos', $subForm);
}
For the create/delete/update part, this article might give some help.
I never found the official approach right for my needs. I developed a completely different approach. In the company where I used to work we used in production this new approach, finding it a bit more elastic and simple. The key concept is "don't use Symfony's Form class and you will discover that embedding forms can be a very simple task"
I hope this can help you embedding forms.

Resources