I have schema like:
Schema:
article:
id: ~
title: { type: VARCHAR, size: '255', required: true }
created_at: { type: TIMESTAMP, required: true }
updated_at: { type: TIMESTAMP, required: true }
article_data:
id: ~
article_data: { type: BLOB, required: true }
article_filename: { type: VARCHAR, size: '255'}
article_id: { type: INTEGER, required: true, foreignTable: article, foreignReference: id, onDelete: cascade }
So, in my article admin module, I'd like to display the article_data widget, which is a file upload.
Everything is fine. But when saving the uploaded file to the server, the article_id field is null.
Is there a way i could get the id of the article and save it as the article_id of the article_data table?
Thanks
EDIT:
I think I need to override the saveEmbeddedForm() method, but I am not sure what I'd need to do.
Could someone help with some code for a saveEmbeddedForm()?
Thanks
I don't known Propel, but in Doctrine you could do something like this:
class ArticleForm extends BaseForm
{
public function configure()
{
parent::configure();
$form = new sfForm();
$datas = $this->getObject()->getDatas();
foreach ($datas as $index => $data)
$form->embedForm($index, new ArticleDataForm($data));
$this->embedForm('dataForms', $form);
}
public function saveEmbeddedForm($con = null, $forms = null)
{
$dataForms = $this->getEmbeddedForm('dataForms')->getEmbeddedForms();
foreach ($dataForms as $dataForm)
$dataForm->getObject()->setArticle($this->getObject());
parent::saveEmbeddedForm($con, $forms);
}
}
Related
I have setup 3 tables in symfony:
A flipbook table, A Skills table, and a relations table, to connect each skill id with each flipbook id.
WHen I built the model, symfony constructed everything correctly, and by default gave a drop-down menu for the skills, which had all skills from the skills table as options. You can select an option and it creates the appropriate relationships.
This is working (sort of). When you submit the form, it does not add the skill_id to the record. It just adds the auto-increment id to the skills relations table and neither the flipbook_id or skill_id. Aaaaaand, if you click more than one checkbox, you get this nice message:
invalid parameter number number of bound variables does not match number of tokens
Whaaaat does that mean? This has got to be a schema issue eh?
How about some code? yes.please.
Schema:
Flipbook:
tableName: flipbook
inheritance:
extends: SvaGeneric
type: concrete
columns:
title: { type: string(255) }
career_associations: { type: clob }
skills: { type: string(255) }
skills_associations: { type: clob }
program: { type: string(255) }
program_associations: { type: clob }
draft_id: { type: integer(10) }
FlipbookSkills:
tableName: flipbook_skills
columns:
title: { type: string(255) }
relations:
Flipbook:
foreignAlias: flipbook_skills
alias: skills
local: title
onDelete: SET NULL
FlipbookSkillRelations:
tableName: flipbook_skill_relations
actAs:
SoftDelete: ~
options:
columns:
flipbook_id: { type: integer(10), notnull: false }
skill_id: { type: integer(10), notnull: false }
relations:
Flipbook:
foreignAlias: flipbook_skills_flipbook
alias: flipbook
local: flipbook_id
onDelete: CASCADE
FlipbookSkills:
foreignAlias: flipbook_skills_skills
alias: flipbookskills
local: skill_id
onDelete: CASCADE
FlipbookSkillsRelationsForm.class.php:
$this->widgetSchema['flipbook_id'] = new sfWidgetFormInputText(array(), array('class' => 'text size-500'));
$this->widgetSchema['skill_id'] = new sfWidgetFormSelectCheckbox(array('choices' => "**WHAT GOES HERE??**"), array('class' => 'text size-500'));
$useFields = array(
'flipbook_id',
'skill_id',
);
$this->useFields($useFields);
Let me know if I should provide further code or explanation.
Here is the FlipbookSkillsTable.class generated in the model:
<?php
/**
* FlipbookSkillsTable
*
* This class has been auto-generated by the Doctrine ORM Framework
*/
class FlipbookSkillsTable extends Doctrine_Table
{
/**
* Returns an instance of this class.
*
* #return object FlipbookSkillsTable
*/
public static function getInstance()
{
return Doctrine_Core::getTable('FlipbookSkills');
}
public function retrieveForFilter()
{
$res = $this->createQuery('s')
->select('s.id, s.name')
->orderBy('s.name ASC')
->execute(array(), Doctrine_Core::HYDRATE_NONE);
// if you want an empty line
$rets = array('' => '');
foreach ($res as $ret)
{
$rets[$ret[0]] = $ret[1];
}
return $rets;
}
public function getAllFlipbookSkills() {
$allSkills = Doctrine_Query::create()
->from('FlipbookSkills')
->orderBy('title ASC')
->execute();
return $allSkills;
}
}
UPDATE
I believe the issue is it is trying to bind the multiple values in the same record. It should make a new record for each skill chosen, like this:
id flipbook_id skill_id
1 2 1
2 2 2
3 2 3
SO I still don't have the schema nailed down correctly, otherwise Doctrine would know how to handle the data correct? Should I explicitly put one to many relationship?
Also, when submitted, it is only adding the flipbook_id value to the record. The skill_id record is 0
FLipbookRelationsForm:
class FlipbookSkillRelationsForm extends BaseFlipbookSkillRelationsForm
{
public function configure()
{
$this->widgetSchema['skill_id'] =
new sfWidgetFormDoctrineChoice(array('model' =>
$this->getRelatedModelName('flipbookskills'),
'expanded' => true, 'multiple' => true, 'add_empty' => false));
$useFields = array(
'flipbook_id',
'skill_id',
);
$this->useFields($useFields);
}
}
Multiple issues (maybe this post can help: Embedded forms in symfony 1.4 not saving propperly)
Your n-m relations doesn't look well defined. Your entities should looks like:
FlipbookSkill:
tableName: flipbook_skill
columns:
title: { type: string(255) }
relations:
Flipbooks:
class: Flipbook
refClass: FlipbookSkillRelation
local: skill_id
foreign: flipbook_id
/* Not foreignAlias and onDelete behaviour defined */
Flipbook:
tableName: flipbook
inheritance:
extends: SvaGeneric
type: concrete
columns:
title: { type: string(255) }
career_associations: { type: clob }
skills: { type: string(255) } # why this????
skills_associations: { type: clob } # why this????
program: { type: string(255) }
program_associations: { type: clob }
draft_id: { type: integer(10) }
relations:
Skills:
class: FlipbookSkill
refClass: FlipbookSkillRelation
local: flipbook_id
foreign: skill_id
FlipbookSkillRelation:
tableName: flipbook_skill_relation
actAs:
SoftDelete: ~
options:
columns:
flipbook_id: { type: integer(10), notnull: false }
skill_id: { type: integer(10), notnull: false }
relations:
Flipbook:
foreignAlias: FlipbookSkills
local: flipbook_id
foreign: id
onDelete: CASCADE
FlipbookSkill:
foreignAlias: FlipbookSkills
local: skill_id
foreign: id
onDelete: CASCADE
Then, you methods/join allowed are:
$flipbookObject->getSkills(); Because flipbook entity n-m relation name "Skills", wich returns a FlipbookSkill DoctrineCollection. Or in query ('f.innerJoin Skills s')
$flipbookObject->getFlipbookSkills(); Because flipbookSkillRelation 1-n foreignAlias name, but probably you will never use this (because you don't really care about relations inself, instead you care about related skills).
... etc
If you have a well defined schema.yml, then your forms should work properly.
Plus:
why are you using and text input widget for an id field (flipbook_id) ????
And why are you using a sfWidgetFormSelectCheckbox if you are working with doctrine entities? You should use sfWidgetFormDoctrineChoice like the BaseEntity defines.
I hope this can help you.
Where are your validators ? Symfony forms don't work properly with missing validators or inappropriate ones. If your validator accept 1-1 relationship, and you send multiple values, it won't work.
Try this :
$this->validatorSchema->setOption('allow_extra_fields' , true);
$this->validatorSchema->setOption('filter_extra_fields' , false);
If your error disappear, then you have a problem with validators. Otherwise you can try to use a the sfEmbedded forms, you render an sfForm, and add multiple time your FlipbooSkillForm with selects. If i remeber well there is some tutorials showing how to add them in js.
http://nacho-martin.com/dynamic-embedded-forms-in-symfony
Perhaps an example would best describe my problem:
Schema:
Referral:
actAs: { timestampable: ~ }
columns:
id: { type: integer, primary: true, notnull: true, autoincrement: true, unique: true }
other_stuff: { type: string }
reasonCode: { type: integer }
relations:
ReasonCode: { local: reasonCode, foreign: id, foreignAlias: ReasonCodes }
ReasonCode:
columns:
id: { type: integer, primary: true, notnull: true, autoincrement: true, unique: true }
description: { type: string }
Query (referralTable.class.php):
public function getObjectByReferralId($id){
$q = Doctrine_Query::create()
->select('*')
->from('referral_submissions')
->where('referral_id=?', $id)
->fetchOne();
return $q;
}
Call in template:
<?php
$id = <source of id>;
echo Doctrine_Core::getTable('referral')->getObjectByReferralId($id)->getReasonCode();
?>
The above call in the template to get the reasoncode returns the "description" stored in the ReasonCode table, not the stored id in the Referral table. I need the actual id, not the joined description. What am I missing?
It's confusing because you named your foreign key with the name of your relation. So when you think you are getting the key, you fetch the relation. And I guess Doctrine do not retrieve the primary key since there is only one field in your ReasonCode table, so it returns the description field.
Try with :
Doctrine_Core::getTable('referral')
->getObjectByReferralId($id)
->get('reasonCode');
By the way, you also can retrieve the id using the relation:
Doctrine_Core::getTable('referral')
->getObjectByReferralId($id)
->getReasonCode()
->getId();
I think you should define your foreign key like : reason_code_id instead of reason_code. Then your schema will become:
Referral:
actAs: { timestampable: ~ }
columns:
id: { type: integer, primary: true, notnull: true, autoincrement: true, unique: true }
other_stuff: { type: string }
reasonCode_id: { type: integer }
relations:
ReasonCode: { local: reasonCode_id, foreign: id, foreignAlias: ReasonCodes }
And you will be able to retrieve the id using:
Doctrine_Core::getTable('referral')
->getObjectByReferralId($id)
->getReasonCodeId();
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
I take many hours to find a solution to save the foreign key in the mother table.
I'm trying to use embed relation with symfony 1.4 (i have ahDoctrineEasyEmbeddedRelationsPlugin too).
When i read the symfony documentation here
http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms
the schema is :
Product:
columns:
name: { type: string(255), notnull: true }
price: { type: decimal, notnull: true }
ProductPhoto:
columns:
product_id: { type: integer }
filename: { type: string(255) }
caption: { type: string(255), notnull: true }
relations:
Product:
alias: Product
foreignType: many
foreignAlias: Photos
onDelete: cascade
the embedRelation looks like:
// lib/form/doctrine/ProductForm.class.php
public function configure()
{
// ...
$this->embedRelation('Photos');
}
In my case i can't do otherway, it's the contrary, the product have the relation keys, i have something like that:
Product:
columns:
name: { type: string(255), notnull: true }
price: { type: decimal, notnull: true }
photoid: { type: integer }
ownerid: { type: integer }
relations:
Photo:
local: photoid
foreign: id
type: one
Owner:
local: ownerid
foreign: id
type: one
Photo:
columns:
id : { type: integer }
filename: { type: string(255) }
caption: { type: string(255), notnull: true }
owner:
columns:
id : { type: integer }
firstname: { type: string(255) }
lastname: { type: string(255), notnull: true }
and the embedRelation:
// lib/form/doctrine/ProductForm.class.php
public function configure()
{
// ...
$this->embedRelation('Photo');
$this->embedRelation('Owner');
}
And there are not widget related to the product like name or price to update in the form but only informations coming from photo and owner table.
When i save the new form, photo and owner object are save in the table, and ids are created with sequences. The problem is that Product is never update. photoid and ownerid still stay to null.
It's like embedded form Photo and Owner are save after the root form.
Is it possible to use embedrelation in that case?
If then what is the correct way to save the foreign keys in Product table when the root form is save in the processForm?
What's the trick, thx for help.
In PhotoForm.class.php use:
$this->useFields(array('filename', 'caption'));
And read the documentation
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.