Symfony3: is it possible to change the name of a form? - php

With Symfony 2.7, you could customize a form's name in your EntityType class with the method getName()
This is now deprecated. Is there another way to do that with Symfony 3.0 ?
I have custom prototype entry_rows for collections that I would need to use in different forms.
Since the name of the rows is based on the form's name, I would need to change the later in order to use them with a different form.

You should implements the getBlockPrefix method instead of getName as described in the migration guide here.
As example:
/**
* Returns the prefix of the template block name for this type.
*
* The block prefix defaults to the underscored short class name with
* the "Type" suffix removed (e.g. "UserProfileType" => "user_profile").
*
* #return string The prefix of the template block name
*/
public function getBlockPrefix()
{
return "form_name";
}
Hope this help

Depending on how your form is built, there is different ways to set the name of your form.
If you are creating the form through $this->createForm(CustomType::class):
$formFactory = $this->get('form.factory');
$form = $formFactory->createNamed('custom_form_name', CustomType::class);
If you are building the form from the controller directly through $this->createFormBuilder():
$formFactory = $this->get('form.factory');
$form = $formFactory->createNamedBuilder('custom_form_name', CustomType::class);
Look at the FormFactory and FormBuilder APIs for more information.

You can try it, remove prefix on field name
public function getBlockPrefix()
{
return null;
}

Related

Setting a global option for all forms in a Laravel application

I want all forms in my laravel application to have 'autocomplete' => 'off' by default, unless I specify 'autocomplete' => 'on'..
Since my application has many forms and laravel is such an awesome framework, I am wondering if I can set a global option of the form class to be always autocomplete off unless I specify the opposite.
Anyone know about this?
Yes, this is possible. The basic idea is that you need to extend two classes: the built in Illuminate\Html\FormBuilder and Illuminate\Html\HtmlServiceProvider classes, and replace the HtmlServiceProvider in app/config/app.php with the one you create.
First, create a MyForm class that is going to override the functionality you want from the FormBuilder:
use Illuminate\Html\FormBuilder;
class MyForm extends FormBuilder
{
/**
* Open up a new HTML form.
*
* #param array $options
* #return string
*/
public function open(array $options = array())
{
// If you haven't specified an autocomplete option, default it to 'off'
if(!isset($options['autocomplete'])) {
$options['autocomplete'] = 'off';
}
return parent::open($options);
}
}
Next, you will need to create a service provider that extends from Illuminate\Html\HtmlServiceProvider and override the registerFormBuilder method:
use Illuminate\Html\HtmlServiceProvider;
use MyForm;
class FormServiceProvider extends HtmlServiceProvider
{
public function registerFormBuilder()
{
$app = $this->app;
// Taken From: Illuminate\Html\HtmlServiceProvider
$app->bindShared('form', function($app) {
// Replace FormBuilder with MyForm
$form = new MyForm($app['html'], $app['url'], $app['session.store']->getToken());
return $form->setSessionStore($app['session.store']);
});
}
}
Finally, you will need to replace 'Illuminate\Html\HtmlServiceProvider', in app/config/app.php with your new Service Provider. I have tested this on a local test Laravel 4.2 install and works without having to modify anything in the calls to Form::open
I was feeling lazy, so I just added the line $attributes['autocomplete'] = 'off'; to the file FormBuilder.php. I added it on line # 108 in the open function.
This is a good example of a hacky solution.

Symfony 1.4 what if I want a form field to be changed something through?

Lets suppose I have a user registration form. Since the password must be stored md5 encoded, the best idea would be to convert it in redhand, but it looks request is read only. How to change a form field during processing?
You can use a custom Validator for the form. Create a class like this:
<?php
class encodeValidator extends sfValidatorBase
{
/**
* #see sfValidatorBase
*/
protected function doClean($value)
{
return md5($value); //md5 can be replaced with another encoding method
}
}
Next, when you create your form, add the custom validator you created, like this:
$this->setWidget('field_name', new sfWidgetFormInputText());
$this->validatorSchema['field_name'] = new encodeValidator();

ZF2 Classmethods Hydrator working with RowGateway

i'm trying to implement the RowGateway class to my entities, I already have a form working with the entity and I'm trying to set the hydrator to work with ClassMethods.
I also noticed that ArraySerializable hydrator calls the populate() method or exchangeArray() and this method set the appropriate primary key when editing a row, unfortunately ClassMethods Hydrator doesn't do that.
What would be the best way to set the correct primary key value when using the Classmethod hydrator, should I set this value before binding the entity to the form? Or, should I extend the Classmethod H. to perform this task on initialize?
I'm not fond of using knowledge of the data layer in my entity. When using exchangeArray() you create mapping in the entity itself. I did some research about Zend's hydrators and came across serval posts including this one. Andrew's example of extending the ClassMethods hydrator seemed a good approach to map column names to getters/setters names.
When extending the ClassMethods hydrator you could also implement Zend\Stdlib\Hydrator\HydratorInterface.
For data manipulation use hydrator strategies.
http://framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.strategy.html
http://juriansluiman.nl/nl/article/125/strategies-for-hydrators-a-practical-use-case
To sepperate your entity over mutliple data sources you can use hydrator filters. For example, by default the ClassMethods hydrator extracts all entity methods starting with get.
http://framework.zend.com/manual/2.1/en/modules/zend.stdlib.hydrator.filter.html
You could extend Zend\Stdlib\Hydrator\ClassMethods and do any transformations you require here, assuming this is what you mean.
You can then use mapField to map from one of your fields to the correct id field name.
namespace Application\Model;
use Zend\Stdlib\Hydrator\ClassMethods;
class MyHydrator extends ClassMethods
{
/**
* Extract values from an object
*
* #param object $object
* #return array
* #throws Exception\InvalidArgumentException
*/
public function extract($object)
{
$data = parent::extract($object);
$data = $this->mapField('id', 'user_id', $data);
return $data;
}
/**
* Map fields
*
* #param type $keyFrom
* #param type $keyTo
* #param array $array
* #return array
*/
protected function mapField($keyFrom, $keyTo, array $array)
{
$array[$keyTo] = $array[$keyFrom];
unset($array[$keyFrom]);
return $array;
}
}
Alternatively you could make a getter and setter for the id field you need setting/getting, for example if you have an id called 'user_id' :
public function getUserId() { .. }
public function setUserId($id) { .. }

symfony2 form creator based on annotations

I'm working on mechanism that will allow me create forms automatically from class annotations.
For example there is a class called "News" with some custom annotations.
/**
* #Admin\Form(name="news")
*/
class News
{
/**
*
* #Admin\Field(name="title", type="text")
*/
private $title;
}
My goal is to write mechanism that will check if exists class with "Form" annotation and create form based on this class fields.
Where should I put this mechanism? First I was thinking about owerwritting FormFactory but I believe there is a better place for such thing, maybe Extension?
There already is a bundle that does what you're asking for: http://knpbundles.com/FlintLabs/FormMetadataBundle
However, if you'd like to create it yourself, you should create a bundle and within it create a custom annotation driver based on the doctrine2 specs (as Symfony uses Doctrine for reading annotations)
In Symfony2, you can add functionality to existing form fields through the use of "Form Type Extensions".
To apply your extension to all field types, set the return value of the getExtendedType() method to "form", i.e:
public function getExtendedType()
{
return 'form';
}
I haven't figured out how to fetch the annotations from the form extension yet.

Symfony2 + Twig: Using an entity type field to store unsaved entities

I've spent a while looking through the symfony2 docs trying to find a suitable method of doing what I need to do, maybe I'm looking in the wrong place.
Basically, I have an entity called Album, which can have many Subalbums associated with it. As the user is creating an Album entity using a form, I want them to be able to create quick Subalbum entities in-line, which will get saved later on. I also want to display the field in a custom format, so I don't want to use a select tag with a multiple attribute, instead I'm manually rendering it in Twig (I haven't had a problem with this).
The relationship on Subalbum is defined as follows:
/**
* #ORM\ManyToOne(targetEntity="Vhoto\AlbumBundle\Entity\Album")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
protected $parent;
Here's what I've tried so far...
Use an entity type field in the form builder, and then manually output the field. The issue I have with using the entity field is that if the user creates a Subalbum inline, symfony2 doesn't like it when I submit the form because it has no ID.
Use a hidden field type and try submitting multiple entries under the same field name (album[subalbums][]). Symfony2 also doesn't like this when I submit the form
I guess I'm going to have to have a prePersist method in my Album entity to create any Subalbum entities that the user has created inline?
Hopefully there is a far more graceful solution that I'm just completely overlooking.
Let me know if anything is unclear.
There is indeed a better way to do it.
Entity creation
Create the two Entity POPOs and assign a many-to-one relationship to one of the fields of the child entity (you have done this correctly). You might also want to define a one-to-many relationship in the parent
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Child", mappedBy="parent", cascade={"persist", "remove" }, orphanRemoval=true)
*/
protected $children;
I am not sure if it's necessary, but you should explicitly set the relationship in your setters just to be sure. For example in your owning entity:
public function addChild(ChildInterface $child)
{
if(!$this->hasChild($child))
{
$this->children->add($child);
$child->setParent($this);
}
}
Doctrine doesn't probably use these methods to bind the post data, but having this for yourself might take care of several persisting issues.
Form type creation
Create a form type for both entities
/**
* This would be the form type for your sub-albums.
*/
class ChildType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
//$builder->add(...);
//...
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'Acme\Bundle\DemoBundle\Entity\Child'
);
}
public function getName()
{
return 'ChildType';
}
}
/**
* This would be the form type for your albums.
*/
class ParentType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
// This part here describes the relationship between the two
// form types.
$builder->add('children', 'collection', array(
'type' => new ChildType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true
));
}
public function getName()
{
return 'ChildType';
}
}
With the options allow_add and allow_delete you've effectively told Symfony, that a user can add or remove entities from the collection. The prototype option lets you have a prototype of the so-called sub-form on your page.
Controller
You should enforce the relationship here as well just to be safe. I have tucked this away in a separate entity manager layer (I have separate managers for more complicated entities), but you can most certainly do this in your controllers as well.
foreach($parent->getChildren() as $child)
{
if($child->getParent() === NULL)
{
$child->setParent($parent);
}
}
View
Prepare your form's template. The prototype should be rendered by calling form_rest(form) somewhere in your template. In case it doesn't or you'd like to customize the prototype, here's a sample of how to do it.
<script id="ParentType_children_prototype" type="text/html">
<li class="custom_prototype_sample">
<div class="content grid_11 alpha">
{{ form_widget(form.children.get('prototype').field1) }}
{{ form_widget(form.children.get('prototype').field2) }}
{{ form_rest(form.children.get('prototype') ) }}
</div>
</li>
</script>
You'd have to make the form dynamic by using JavaScript. If you use jQuery, you can access the prototype by calling $('ParentType_children_prototype').html(). It is important to replace all occurrences of $$name$$ in the prototype with the proper index number when adding a new child to the parent.
I hope this helps.
EDIT I just noticed there's an article in the Symfony2 Form Type reference about CollectionType. It has a nice alternative on how to implement the front-end for this.

Categories