Selected attributes of genemu_jqueryselect2_entity not stored to database - symfony-forms

I'm using genemu_jqueryselect2_entity for a multiple selection field within a form (located in an Sonata admin class) for a so called Uni (university) entity:
->add('courses', 'genemu_jqueryselect2_entity',array('multiple' => true, 'class' => 'PROJECT\UniBundle\Entity\Course'))
But the selected entries are not filled into my entity. With firebug I was able to detect, that the ids of the courses are passed correctly via POST.
Maybe the field is not correctly mapped to the Uni entity, but I have no idea why.
This is the adding method of my Uni entity, which doesn't even get called:
public function addCourse(\PROJECT\UniBundle\Entity\Course $courses)
{
$this->courses[] = $courses;
return $this;
}
How can I get the field to be mapped with the courses attribute of Uni? How could I debug this?
Any help will be appriciated!

Try writing that method like this:
public function addCourse(\PROJECT\UniBundle\Entity\Course $course)
{
$this->courses[] = $course;
$course->setUniversity($this); // Or similar.
return $this;
}
Otherwise foreign key is not set on a course row in the DB.

Try to create method setCourses
public function setCourses(\Doctrine\Common\Collections\Collection $courses)
{
$this->courses = $courses;
...

I don't know why, but the method addCourse isn't called.
Anyway, Tautrimas Pajarskas's answer was usefull to me so I gave an upvote.
The foreign key relationship was the necessary and missing part of my code.
I implemented it in the university sonata admin like this:
private function addUniToCourses ($university) {
foreach($university->getCourses() as $course) {
if(!$course->getUniversities()->contains($university)) {
$course->addUniversity($university);
}
}
}
public function prePersist($university) {
$this->addUniToCourses($university);
}
public function preUpdate($university) {
$this->addUniToCourses($university);
}
This was the solution to my problem.

I had the same problem a while ago: Symfony2, $form->bind() not calling adder methods of entity
Solution:
For the adder (addCourse()) to be called, you have to disable the by_reference option of the field:
->add('courses', 'genemu_jqueryselect2_entity',
array(
'by_reference' => false, // This line should do the trick
'multiple' => true,
'class' => 'PROJECT\UniBundle\Entity\Course'))

Related

How do I populate Doctrine fields from PHP to complement form data?

I have a form which sends data from the server, which works just fine in Symfony, but what I don't know, is how can I set some fields by myself, like the user_id field, which I clearly don't want the user to send, I want to add the value for that just before Doctrine validates it.
Any relevant documentation links would be also greatly appreciated.
Unset your fields in configure method:
class YourForm extends BaseYourForm
{
public function configure()
{
unset($this['user_id']);
}
}
Override save method of Your model:
class Your extends BaseYour
{
public function save(Doctrine_Connection $conn = null)
{
$this->setUserId($whatever);
parent::save($conn);
}
}

symfony admin filter with join

I have a table, heading, that has an import_profile_id. import_profile has a bank_id.
On my headings list page in my admin, I'd like to add the ability to filter by bank_id. However, since heading doesn't have a bank_id - it needs to go through import_profile to get that - I can't just add a bank_id field and expect it to work.
Can anyone explain how to do this? The closest thing I've found is this post but I don't think it really addresses my issue.
This can be done by using virtual columns like the post you found. The virtual column is a way to add a new criteria to filter using the autogenerated filter provided by symfony. It works like this:
1 - Go to the generator.yml of the admin module and add the name of the virtual column that will create and add
<!-- apps/backend/modules/module_name/config/generator.yml -->
filter:
[virtual_column_name, and, other, filter, columns]
2 - In your lib/filter/{TableName}FormFilter.class.php (I think in your case must be HeadingFormFilter) you have to define that virtual column in the configure() method
public function configure()
{
//Type of widget (could be sfWidgetFormChoice with bank names)
$this->widgetSchema['virtual_column_name'] = new sfWidgetFormInputText(array(
'label' => 'Virtual Column Label'
));
//Type of validator for filter
$this->validatorSchema['virtual_column_name'] = new sfValidatorPass(array ('required' => false));
}
3 - Override the getFields() of that class to define it in the filter and set the filter function
public function getFields()
{
$fields = parent::getFields();
//the right 'virtual_column_name' is the method to filter
$fields['virtual_column_name'] = 'virtual_column_name';
return $fields;
}
4 - Finally you have to define the filter method. This method must be named after the add...ColumnQuery pattern, in our case must be addVirtualColumnNameColumnQuery(not a happy name choice :P), so
public function addVirtualColumnNameColumnQuery($query, $field, $value)
{
//add your filter query!
//for example in your case
$rootAlias = $query->getRootAlias();
$query->innerJoin($rootAlias . '.ImportProfile ip')
->andWhere('ip.BankId = ?', $value);
//remember to return the $query!
return $query;
}
Done! You can know filter by bank_id.

Symonfy 1.4 dynamic validation possible?

I'm trying to create a form that change the validation of a field based on the select option from the html form field.
Ex: if user select a option 1 from drop down field "options", I want the field "metric" to validate as sfValidatorInteger. If user select option 2 from field "options", I want the field "metric" to validate as sfValidatorEmail, etc.
So inside the public function configure() { I have the switch statement to capture the value of "options", and create the validator based on that value returned from the "options".
1.) How do I capture the value of "options" ? I've tried:
$this->getObject()->options
$this->getTaintedValues()
The only thing that's currently working for me is but it's not really MVC:
$params = sfcontext::getInstance()->getRequest()->getParameter('options');
2.) Once I've captured that information, how can I assign the value of "metric" to a different field? ("metric" is not a real column in db). So I need to assign the value of "metric" to different field such as "email", "age" ... Currently I'm handling this at the post validator like this, just wondering if I can assign value within the configure():
$this->validatorSchema->setPostValidator(new sfValidatorCallback(array('callback' => array($this, 'checkMetric'))));
public function checkMetric($validator, $values) {
}
Thanks!
You want to use a post validator. Try doing something like this in your form:
public function configure()
{
$choices = array('email', 'integer');
$this->setWidget('option', new sfWidgetFormChoice(array('choices' => $choices))); //option determines how field "dynamic_validation" is validated
$this->setValidator('option', new sfValidatorChoice(array('choices' => array_keys($choices)));
$this->setValidator('dynamic_validation', new sfValidatorPass()); //we're doing validation in the post validator
$this->mergePostValidator(new sfValidatorCallback(array(
'callback' => array($this, 'postValidatorCallback')
)));
}
public function postValidatorCallback($validator, $values, $arguments)
{
if ($values['option'] == 'email')
{
$validator = new sfValidatorEmail();
}
else //we know it's one of email or integer at this point because it was already validated
{
$validator = new sfValidatorInteger();
}
$values['dynamic_validation'] = $validator->clean($values['dynamic_validation']); //clean will throw exception if not valid
return $values;
}
1) In a post validator, values can be accessed by using the $values parameter. Just use $values['options'] and it should be fine... or did you want to access this values from another part of you code? $this->getObject()->widgetSchema['options'] should work too I think, once your form is bound to an object.
2) The configure() method is called at the end of the form constructor, so values are not bound nor accessible yet, unless you are initializing your form with an object from the db (which does not require any validation). But if you want to initialize your form from $_POST, a post validator is definitely the way to go IMHO.
I got the validation error to appear alongside the field by throwing a sfValidatorErrorSchema instead of a sfValidatorError.
$values['dynamic_validation'] = $validator->clean($values['dynamic_validation']);
…becomes…
try
{
$values['dynamic_validation'] = $validator->clean($values['dynamic_validation']);
}
catch(sfValidatorError $e)
{
$this->getErrorSchema()->addError($e, 'dynamic_validation');
throw $this->getErrorSchema();
}
Not sure if this is the best way to get this result, but it seems to be working for me at the moment.

Help With Embedded BaseFormDoctrine Forms

I have the following code blocks:
class MerchantStoreForm extends sfForm
{
public function configure()
{
$this->disableCSRFProtection();
$this->setWidgets(array(
'brand_id' => new sfWidgetFormDoctrineChoice(array('label'=> 'Store Brand','model'=>'Brand','add_empty'=>'-Select Brand-','method'=>'getName','key_method'=>'getId','order_by'=>array('name','asc'))),
'newbrand' => new sfWidgetFormInputCheckbox(array('label' => 'New'),array('value'=>'Y'))
));
$this->setValidators(array(
'newbrand' => new sfValidatorString(array('required'=>false)),
'brand_id' => new sfValidatorDoctrineChoice(array('model'=>'Brand'))
));
$brand = new Brand();
$brand_form = new BrandForm();
$brand_form->widgetSchema['name']->setAttribute('style','display:none');
$this->embedForm('brand', $brand_form);
$this->getWidgetSchema()->setNameFormat('store[%s]');
}
public function execute()
{
$form_values = $this->getValues();
if($form_values['newbrand'])
{
$brand_form = $this->getEmbeddedForm('brand');
$brand_form->save();
$brand = $brand_form->getObject();
}
else
{
$brand = doctrine::getTable('Brand')->findOneById($form_values['brand_id']);
}
return $brand->getId();
}
}
Two questions:
1) The magic of $brand_form->save() doesn't work for me. I get a 500 Internal Server Error sfValidatorErrorSchema error pointing to the following piece of code in my symfony generated BaseBrandForm.class.php:
...
$this->widgetSchema->setNameFormat('brand[%s]');
$this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);
...
This works instead in replacement though:
$brand_form->updateObject($form_values['brand']);
$brand_form->getObject()->save();
Why is this?
2) Why do I get an undefined method error while calling getter method on the object of the BaseFormDoctrine embedded form:
return $brand->getId();
Thanks in advance for your help.
Sharmil
1) BrandForm throws an exception because it doesn't have any values. Classes that extend sfFormObject don't play nicely when embedded directly into non object forms (like sfForm).
What is MerchantStoreForm doing? Depending on the situation, it should probably be extending sfFormObject or BrandForm should be the top level form. If this isn't possible, you'll have to write add a save method to MerchantStoreForm that calls updateObject and save. To better understand what's happening, go through the logic that takes place in sfFormObject - it's worth knowing especially if you're using embedded forms.
2) No clue here. I would see what $brand is actually an instance of. If it's a record and that record has an id field, there's no reason that shouldn't work.

Passing values from action class to model class in symfony

I have a action class for saving some data to a database.In action class iam getting a id through url.I have to save the id in table.
I am getting the id by $request->getParameter('id')
I used this code for saving
$this->form->bind($request->getParameter('question_answers'));
if ($this->form->isValid())
{
$this->form->save();
$this->redirect('#homepage');
}
in model class i used a override save method to save extra field
public function save(Doctrine_Connection $conn = null)
{
if ($this->isNew())
{
$now=date('Y-m-d H:i:s', time());
$this->setPostedAt($now);
}
return parent::save($conn);
so i want to get the id value here .
So how can i pass id value from action class to model class
is any-other way to save the id in action class itself
Thanks in advance
Just use
$this->form->getObject()->setQuestionId($request->getParameter('id'));
$this->form->save();
QuestionId=field name
$request->getParameter('id')= is the default value
Most probably not the best solution but you can save that id value in a session variable and use it later anywhere you need. something like:
$this->getUser()->setAttribute('id', $request->getParameter('id')); // in action class
and
sfContext::getInstance()->getUser()->getAttribute('id'); // in model class
If your model has a field for this ID already (eg in your example posted_id), then you can do one of 2 things:
1: Pass the ID to your form when you create it, as an option:
$this->form = new MyForm(array(), array("posted_id" => $id));
and then you can override your form's doSave() method to set the field:
// ...
$this->getObject()->posted_id = $this->getOption("posted_id");
// ...
or similar
2: Add a widget to your form in the configure() method with a corresponding validator, and pass in the ID when you get the values from the request:
$values = $request->getParameter('question_answers');
$values["posted_id"] = $request->getParameter("id");
$this->form->bind($values);
If you're rendering your form manually, just don't render this new field in your view template.
Either way, saving the model as a result of the form saving it will include this new field I believe. The second one is from memory, but it should technically work... :-)
Best practice I'm using consists of 2 steps:
Pass additional data to forms' options (2nd constructor's parameter or just $form->setOption('name', 'value'));
override protected method doUpdateObject in way like this:
protected function doUpdateObject($values)
{
$this->getObject()->setFoo($this->getOption('foo'));
parent::doUpdateObject($values);
}
Enjoy!
You could use (sfUser)->setFlash and (sfUser)->getFlash instead of setAttribute, it's more secure...

Resources