My entity has a property from type simple_array, storing a list of strings which are generated by the user (so choice does not apply).
The relevant part from the entity:
/**
* #var array
*
* #ORM\Column(type="simple_array")
*/
private $tags;
I would like to use the SonataAdminBundle to show, create and edit the entity with the tags present:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('tags', 'collection');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('tags', 'array');
}
The list works, but shows [0 => Tag1, 1 => Tag2] where I'd rather show Tag1, Tag2. The create and edit does not work at all, showing nothing where the input field for the tags should be.
To be clear: Tags are not a related entity, they are simply an array of strings!
For add/edit your tags I recommend this general solution How to add an array (customisable) to a symfony2 form (with sonata Admin)?
For customization of array values (by defaults) in list mode as you need, just overwrites the list_array.html.twig template from SonataAdminBundle, to something like this:
{% extends admin.getTemplate('base_list_field') %}
{% block field %}
{{ value|join(', ') }}
{% endblock %}
Related
I have created the front end relationship between my models image and album by using hasMany and belongsTo however I am not getting any results displayed and
{{ dump(images) }} and {{ dump(albums) }} gives Object variables NULL 0 NULL
Any Ideas why the relationship is not being picked up?
Album
public $hasMany = [
'images' => [ 'Acme\Library\Models\Image' ,'key'=>'album_id'],
];
Image
public $belongsTo = [
'albums' => ['Acme\Library\Models\Album']
];
album-details.htm
{{ dump(images) }}
{% for image in images %}
{{image.id|raw }}
{% endfor %}
if I run a dump on {{ dump(record) }} I get
Object variables
Acme\Library\Models\Album
bootValidation() method Boot the validation trait for this model.
forceSave() method Force save the model even if validation fails.
validate() method Validate the model instance
isAttributeRequired() method Determines if an attribute is required based on the validation rules.
errors() method Get validation error message collection for the Model
validating() method Create a new native event for handling beforeValidate().
validated() method Create a new native event for handling afterValidate().
rules array(0)
timestamps boolean
table string
relationConfig string
hasMany array(1)
I can see using the builder plugin that record is defined {% set records = builderList.records %}
Does albums/images need to be defined in this manner also?
I you send record variable to the view and record is an instance of Album you can access your images this way.
{% for image in record.images %}
{{image.id|raw }}
{% endfor %}
I have two tables in database - orders and trucks. They have relation ManyToOne. When I receive order - truck field is null by default. Trucks table have preconfig data. So my task: for every order entity which I restore with findAll() method and send to twig (render as table) make checkbox, so I can appoint truck to every order.
Here is form which appoint truck to order:
class TruckType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'entity', array(
'class' => 'AppBundle:Trucks',
'choice_label' => 'name',
'label' => false,
'multiple' => false,
'required' => false,
));
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Orders',
));
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
public function getName()
{
return 'TruckType';
}
}
And twig:
{% for order in orders %}
<tr>
<td width="30%">{{ order.name }}</td>
<td width="30%">{{ order.delivery }}</td>
<td width="15%">{{ order.stock }}</td>
<td width="15%">{{ form_widget(truckform) }}</td>
</tr>
{% endfor %}
But this render checkbox only for first entity in table. Where I am wrong?
You did not show the code for how you generate your truckform variable but I guess you just instantiate a form using your TruckType form type. The problem with this approach is that Symfony will not render a form or a form field more than once, so that your {{ form_widget(truckform) }} fragment is only rendered the first loop iteration. If you think about it, it makes sense, otherwise you will have a number of form fields all with the same name and id and when you submitted your form, there will be no way to differentiate them. You need to take a different approach, using embedded forms. Take a look at this Symfony doc page which will point you in the right direction: How to Embed a Collection of Forms
I have an entity Nace that holds hierarchical data. It is mapped as a many-to one association to an entity Organisation.
class Organisation
{
/**
* #ORM\ManyToOne(targetEntity="Pftp\UserBundle\Entity\Nace")
* #ORM\JoinColumn(name="nace_id", referencedColumnName="id")
*/
private $nace;
}
The following form renders the nace property as a drop-down with all records in Nace as expected (showing my starting point).
class OrganisationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('nace')
;
}
}
As Nace contains several hundreds of records, I want to implement a custom FieldType
which adds a second entity choice field with higher level options from the same table (categories)
which populates the main choice field via ajax when the user selects a category
When I add the naceCategory child field and try to render the form, I get the an error related to data mapping.
In order to figure the data flow, I added a ModelTransformer but the methods transform and reverseTransform are never called. Here is my code:
Template
{% block nace_widget %}
{{ form_widget(form) }}
{{ form_widget(form.naceCategory) }}
{% endblock %}
NaceType
class NaceType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'compound' => true,
'mapped' => true,
'required' => true,
'class' => 'Pftp\UserBundle\Entity\Nace',
'query_builder' => function(NaceRepository $er) {
return $er->createQueryBuilder('node')->where('1=0');
},
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add($builder->create('naceCategory', 'entity', array(
'mapped' => false,
'required' => false,
'class' => 'Pftp\UserBundle\Entity\Nace',
'query_builder' => function(NaceRepository $er) {
return $er->getCategoriesQueryBuilder();
}
)))
->addModelTransformer(new NaceTransformer())
;
}
public function getParent()
{
return 'entity';
}
public function getName()
{
return 'nace';
}
}
Error
Expected argument of type "object, array or empty", "string" given
at PropertyPathMapper ->mapDataToForms ('', object(RecursiveIteratorIterator))
in W:\Symfony2\Sources\pftpprod\vendor\symfony\symfony\src\Symfony\Component\Form\Form.php at line 385 +
at Form ->setData (null)
in W:\Symfony2\Sources\pftpprod\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper.php at line 57 +
at PropertyPathMapper ->mapDataToF...
Analogy
Let me try and give you a better idea of what I'm trying to achieve as this Nace thing is pretty unknown.
Basically, I need 2 cascading drop-downs. Imagine you have a list of all the cities of the world in one table and you want the user to choose only 1. As the list is too long, you would want the user to first choose a country from another drop-down (not mapped with your target entity), which should populate your city drop-down with a limited list of cities.
Well, in my case nace corresponds to the city drop-down (initially empty), and naceCategory corresponds to the country drop-down (always populated with the repo method getCategoriesQueryBuilder()). The only difference to the city/country exmaple is, that both drop-downs are hydrated from the same table/entity (Nace), just with values from different hierachy levels.
Does anybody know what I'm missing here?
Is there way to show url with filter to another entity list instead of showing all related entities?
My entity has OneToMany reference to it's events:
/**
*
* #ORM\OneToMany(targetEntity="Event", mappedBy="organizer", cascade={"ALL"})
*/
private $events;
$formMapper->add('events') shows me select2 input with all events.
I just want to show a link to events list with filter to current owner.
I'm using Symfony 2.5.
Yes, this is possible.
You have to get the current owner and create a custom query builder to get only the events related to the owner identifier.
protected function configureFormFields(FormMapper $formMapper)
{
// get current owner
$ownerId = $this->subject->getId();
// using query_builder to create a custom query based on current owner
$formMapper->add('events', null, array(
'query_builder' => function(EntityRepository $er) use ($ownerId) {
$events = $er->createQueryBuilder('e');
if ($ownerId != null) {
$events = $er->where('e.owner = :ownerId')
->setParameter('ownerId', $ownerId);
}
return $events;
}
));
}
Also don't forget to add the use for EntityRepository :
use Doctrine\ORM\EntityRepository;
I have a user entity and a log entity. I have a OneToMany connection from User to Log.
In my Log Table are stored log entries according to users.
I output a User list:
Controller:
$user = $this->getDoctrine()
->getRepository('SeotoolMainBundle:User')
->findBy(array('isActive' => 1, 'isAdmin' => 0));
TWIG
{% for user in list_user %}
{{ user.username }}
{% endfor %}
Now I want to recieve ONE row of the log table, sorted by a field called "date" and return it in the for user loop like this:
{% for user in list_user %}
{{ user.username }}
{{ user.log.lastEntry
{% endfor %}
How do I do this?
Entity User.php:
/**
* #ORM\OneToMany(targetEntity="Log", mappedBy="user")
* #ORM\OneToMany(targetEntity="Log", mappedBy="editor")
*/
protected $log;
Entity Log.php
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="log")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
protected $User;
Assuming that your getter for accessing the Log Collection object is a getLogMessages method, and assuming you can access the log message with a getMessage method, the following should solve it:
{{ User.getLogMessages().last().getMessage() }}
The last method gives you access to the last element stored in the collection.
By the way, you should use the OrderBy annotation, otherwise the order must be considered undefined. (Although, in reality, you will usually get the elements in the order they were inserted).
I solved it now in TWIG. But this isn't nice at all... I would prefere a solution inside of the controller and not in the VIEW.
{% for log in last_userlog|reverse if(log.user.Id == user.Id) %}
{% if loop.index == 1 %}
{{ log.logTitle }}
{% endif %}
{% endfor %}