Sonata admin ignores "required" attribute - php

I am working on a Symfony 2.7 application that uses the Sonata admin bundle. In one of my entities' configureFormFields() method, I have the following:
->add('market',
'entity',
array(
'multiple' => true,
'class' => 'MyCompany\AppBundle\Entity\Market',
'choices' => $query = $this
->entityManager
->getRepository(Market::class)
->findBy(['status' => 100])
,
'placeholder' => 'no_selection',
'required' => true,
'label' => $this->trans('country_of_origin', [], 'messages'),
'attr' => array(
'class' => 'jsb_ jsb_GetDealersForCountry jsb_HideOtherTabs',
'data-jsb' => json_encode(array(
'url' => $this->getRequest()->getBaseUrl() . '/dealers/country/id/'
)),
)
)
)
... but I find that I am able to save without any value in the 'market' field.
Here is the annotation over the market property in my entity:
/**
*
* #ORM\ManyToMany(targetEntity="MyCompany\AppBundle\Entity\Market", cascade={"persist"})
*
*/
private $market;
So anyway, is there a simple way to make this field truly mandatory?

Please Refer below link and create your custom validation for market field in admin class
sonata admin validation

Here is how my annotations now look:
/**
*
* #ORM\ManyToMany(targetEntity="MyCompany\AppBundle\Entity\Market", cascade={"persist"})
* #Assert\Count(min=1)
*
*/
private $market;
This change comes courtesy of the Symfony documentation:
https://symfony.com/doc/2.8/reference/constraints/NotNull.html

Related

Zend3 Form Filter - ParamConverter could not generate valid form

I'm really confused about my Form Filter.
My Test-Project contains 2 Models.
class Category extends AbstractEntity
{
use Nameable; // just property name and getter and setter
/**
* #var boolean
* #ORM\Column(name="issue", type="boolean")
*/
private $issue;
/**
* #var Collection|ArrayCollection|Entry[]
*
* #ORM\OneToMany(targetEntity="CashJournal\Model\Entry", mappedBy="category", fetch="EAGER", orphanRemoval=true, cascade={"persist", "remove"})
*/
private $entries;
}
the entry
class Entry extends AbstractEntity
{
use Nameable;
/**
* #var null|float
*
* #ORM\Column(name="amount", type="decimal")
*/
private $amount;
/**
* #var null|Category
*
* #ORM\ManyToOne(targetEntity="CashJournal\Model\Category", inversedBy="entries", fetch="EAGER")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false)
*/
protected $category;
/**
* #var null|DateTime
*
* #ORM\Column(name="date_of_entry", type="datetime")
*/
private $dateOfEntry;
}
And if someone needed the AbstractEntity
abstract class AbstractEntity implements EntityInterface
{
/**
* #var int
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
}
Every Category can have many Entries. I'm using Doctrine for this relation. And this works fine.
I have a Form based on this FieldSet:
$this->add([
'name' => 'id',
'type' => Hidden::class
]);
$this->add([
'name' => 'name',
'type' => Text::class,
'options' => [
'label' => 'Name'
]
]);
$this->add([
'name' => 'amount',
'type' => Number::class,
'options' => [
'label' => 'Summe'
]
]);
$this->add([
'name' => 'date_of_entry',
'type' => Date::class,
'options' => [
'label' => 'Datum'
]
]);
$this->add([
'name' => 'category',
'type' => ObjectSelect::class,
'options' => [
'target_class' => Category::class,
]
]);
So my Form displays a dropdown with my categories. Yeah fine.
To load the Category for my Entry Entity i use a filter.
$this->add([
'name' => 'category',
'required' => true,
'filters' => [
[
'name' => Callback::class,
'options' => [
'callback' => [$this, 'loadCategory']
]
]
]
]);
And the callback:
public function loadCategory(string $categoryId)
{
return $this->mapper->find($categoryId);
}
The mapper loads the category fine. great. But the form is invalid because:
Object of class CashJournal\Model\Category could not be converted to int
Ok, so i'm removing the Filter, but now it failed to set the attributes to the Entry Entity, because the setter needs a Category. The Form error says:
The input is not a valid step
In Symfony i can create a ParamConverter, which converts the category_id to an valid Category Entity.
Question
How i can use the filter as my ParamConver?
Update
Also when i cast the category_id to int, i will get the error from the form.
Update 2
I changed my FieldSet to:
class EntryFieldSet extends Fieldset implements ObjectManagerAwareInterface
{
use ObjectManagerTrait;
/**
* {#inheritDoc}
*/
public function init()
{
$this->add([
'name' => 'id',
'type' => Hidden::class
]);
$this->add([
'name' => 'name',
'type' => Text::class,
'options' => [
'label' => 'Name'
]
]);
$this->add([
'name' => 'amount',
'type' => Number::class,
'options' => [
'label' => 'Summe'
]
]);
$this->add([
'name' => 'date_of_entry',
'type' => Date::class,
'options' => [
'label' => 'Datum'
]
]);
$this->add([
'name' => 'category',
'required' => false,
'type' => ObjectSelect::class,
'options' => [
'target_class' => Category::class,
'object_manager' => $this->getObjectManager(),
'property' => 'id',
'display_empty_item' => true,
'empty_item_label' => '---',
'label_generator' => function ($targetEntity) {
return $targetEntity->getName();
},
]
]);
parent::init();
}
}
But this will be quit with the error message:
Entry::setDateOfEntry() must be an instance of DateTime, string given
Have you checked the documentation for ObjectSelect? You appear to be missing a few options, namely which hydrator (EntityManager) and identifying property (id) to use. Have a look here.
Example:
$this->add([
'type' => ObjectSelect::class,
'name' => 'category', // Name of property, 'category' in your question
'options' => [
'object_manager' => $this->getObjectManager(), // Make sure you provided the EntityManager to this Fieldset/Form
'target_class' => Category::class, // Entity to target
'property' => 'id', // Identifying property
],
]);
To validate selected Element, add in your InputFilter:
$this->add([
'name' => 'category',
'required' => true,
]);
No more is needed for the InputFilter. A Category already exist and as such has been validated before. So, you should just be able to select it.
You'd only need additional filters/validators if you have special requirements, for example: "A Category may only be used once in Entries", making it so that you need to use a NoObjectExists validator. But that does not seem to be the case here.
UPDATE BASED ON COMMENTS & PAST QUESTIONS
I think you're over complicating a lot of things in what you're trying to do. It seems you want to simply populate a Form before you load it client-side. On receiving a POST (from client) you wish to put the received data in the Form, validate it and store it. Correct?
Based on that, please find a complete controller for User that I have in one of my projects. Hope you find it helpful. Providing it because updates are veering away from your original question and this might help you out.
I've removed some additional checking and error throwing, but otherwise is in complete working fashion.
(Please note that I'm using my own abstract controller, make sure to replace it with your own and/or recreate and match requirements)
I've also placed additional comments throughout this code to help you out
<?php
namespace User\Controller\User;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\ORMException;
use Exception;
use Keet\Mvc\Controller\AbstractDoctrineActionController;
use User\Entity\User;
use User\Form\UserForm;
use Zend\Http\Request;
use Zend\Http\Response;
class EditController extends AbstractDoctrineActionController
{
/**
* #var UserForm
*/
protected $userEditForm; // Provide this
public function __construct(ObjectManager $objectManager, UserForm $userEditForm)
{
parent::__construct($objectManager); // Require this in this class or your own abstract class
$this->setUserEditForm($userEditForm);
}
/**
* #return array|Response
* #throws ORMException|Exception
*/
public function editAction()
{
$id = $this->params()->fromRoute('id', null);
// check if id set -> else error/redirect
/** #var User $entity */
$entity = $this->getObjectManager()->getRepository(User::class)->find($id);
// check if entity -> else error/redirect
/** #var UserForm $form */
$form = $this->getUserEditForm(); // GET THE FORM
$form->bind($entity); // Bind the Entity (object) on the Form
// Only go into the belof if() on POST, else return Form. Above the data is set on the Form, so good to go (pre-filled with existing data)
/** #var Request $request */
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost()); // Set received POST data on Form
if ($form->isValid()) { // Validates Form. This also updates the Entity (object) with the received POST data
/** #var User $user */
$user = $form->getObject(); // Gets updated Entity (User object)
$this->getObjectManager()->persist($user); // Persist it
try {
$this->getObjectManager()->flush(); // Store in DB
} catch (Exception $e) {
throw new Exception('Could not save. Error was thrown, details: ', $e->getMessage());
}
return $this->redirectToRoute('users/view', ['id' => $user->getId()]);
}
}
// Returns the Form with bound Entity (object).
// Print magically in view with `<?= $this->form($form) ?>` (prints whole Form!!!)
return [
'form' => $form,
];
}
/**
* #return UserForm
*/
public function getUserEditForm() : UserForm
{
return $this->userEditForm;
}
/**
* #param UserForm $userEditForm
*
* #return EditController
*/
public function setUserEditForm(UserForm $userEditForm) : EditController
{
$this->userEditForm = $userEditForm;
return $this;
}
}
Hope that helps...

Modifying label for symfony generated form

I currently have a working form in Symfony where I have a list of companies with checkboxes next to each company name. This is so you can check off which company is assigned to each user. The checkbox currently shows the accountID but it would also be helpful to have the entity field 'name' as well. Can you build a property with two entity fields? Here is my form in my controller:
->add('companies', 'entity', array(
'label' => 'Company',
'class' => 'Default\Bundle\Entity\Customer',
'property' => 'accountId', //This puts the company id next to the check box
'multiple' => true,
'expanded' => true,
'query_builder' => function ($repository)
{
return $repository->createQueryBuilder('c')->orderBy('c.accountId', 'ASC');
},))
->add('Save', 'submit')
->getForm();
This is what I am trying to do:
->add('companies', 'entity', array(
'label' => 'Company',
'class' => 'Default\Bundle\Entity\Customer',
'property' => 'accountId' + 'name', // I want to combine my entity fields here
'multiple' => true,
'expanded' => true,
'query_builder' => function ($repository)
here is the entity just for reference
class Customer
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Assert\NotBlank(message="Please enter a Customer ID.")
* #Assert\Length(max="32")
* #ORM\Column(type="string", length=32)
* #var string
*/
protected $accountId;
/**
* #Assert\NotBlank(message="Please enter a company name.")
* #Assert\Length(max="60")
* #ORM\Column(type="string", length=60)
* #var string
*/
protected $name;
And one last time... I want to go from this:
To this:
Create a simple getter and use that as the property, eg:
public function getNamePlusAccountId()
{
return $this->name." (".$this->accountId.")";
}
and use 'property' => 'namePlusAccountId' in your form.
If you only need to change the label but would like to keep the form field value then http://symfony.com/doc/current/reference/forms/types/entity.html#choice-label probably what are you looking for

ZF2 Form / Doctrine Self Referring Relation Dropdown

First of all thanks for looking into this.
I'm building a form to add categories to the db table and these categories can have a parent category (self referring). there is a dropdown to select the parent category. I'm using ZF2 and Doctrine 2 to build this form. Everything works fine but the only issue i have is that on the edit page, the parent category dropdown it shows the current category as well. I'd like to know how to exclude it from the dropdown. I'm posting some of my codes below. To keep it simple i removed some unrelated lines and shorten some names.
I defined the self referring relationship on the model
//Category model
use Doctrine\ORM\Mapping as ORM;
Class Category {
/**
*
* #var integer
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
.....
.....
/**
* Parent category if available
* #var self
* #ORM\OneToOne(targetEntity="Category")
* #ORM\JoinColumn(name="parent", referencedColumnName="id", nullable=true)
*/
protected $parent;
On the form I have a dropdown listing all the categories
$parent = new \DoctrineModule\Form\Element\ObjectSelect('parent');
$parent->setOptions(array(
'label' => 'Parent Category',
'class' => '',
'object_manager' => $this->em,
'target_class' => \Data\Entity\Category::class,
'property' => 'name',
'display_empty_item' => true,
'empty_item_label' => '- Select Category -',
'required' => false
))->setAttributes(array(
'class' => 'form-control'
));
On the edit controller i load the form and and bind it to the db entry
public function editAction()
{
//get id from url
$id = $this->params()->fromRoute('id', 0);
$request = $this->getRequest();
//load selected category from database
$category = $this->em->getRepository(\Data\Entity\Category::class)->find($id);
//create form
$form = new Form\Category($this->em);
//bind selected category to form
$form->bind($category);
......
}
Thanks.
You need to pass the category id of the category being edited to the form and set object selects search params to pass the id to the entity repository. You will then need to create a search query in the repository to exclude the category id from being returned in the search results.
You can pass the category id to the form with a simple setter.
protected $categoryId;
public function setCategoryId($categoryId)
{
$this->categoryId = $categoryId;
}
In your form you will need something like
$parent->setOptions(array(
'label' => 'Parent Category',
'class' => '',
'object_manager' => $this->em,
'target_class' => \Data\Entity\Category::class,
'property' => 'name',
'is_method' => true,
'find_method' => array(
'name' => 'findCategories',
'params' => array(
'searchParams' => array('id' => $this->categoryId),
),
),
'display_empty_item' => true,
'empty_item_label' => '- Select Category -',
'required' => false
))->setAttributes(array(
'class' => 'form-control'
));
and in your categories repository
public function findCategories($searchParams)
{
$builder = $this->getEntityManager()->createQueryBuilder();
$builder->select('c')
->from(\Data\Entity\Category::class, 'c')
->where('c.id != ?1')
->setParameter(1, $searchParams['id'])
->orderBy('c.category', 'ASC');
return $builder->getQuery()->getResult(Query::HYDRATE_OBJECT);
}
note the orderBy is optional.
I hope this makes sense.

Is it possible to add a translatable association in Sonata Admin Bundle?

Is it possible to add a translatable association in Sonata Admin, using DoctrineBehaviors Translatable feature?
I mean, something like that:
// InfoPageAdmin.php
->add('translations', 'a2lix_translations', [
'fields' => [
'title' => [
'field_type' => 'text'
],
'content' => [
'field_type' => 'ckeditor',
'config_name' => 'default'
],
'slideshow' => [
'field_type' => 'sonata_type_model_list'
]
]
])
Where 'slideshow' is translatable field, associated with other entity:
// InfoPageTranslation.php
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\PictureCollection", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="slideshow_id", referencedColumnName="id")
*/
protected $slideshow;
I got the following error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to
Sonata\AdminBundle\Form\DataTransformer\ModelToIdTransformer::__construct()
must implement interface
Sonata\AdminBundle\Model\ModelManagerInterface, null given, called in
D:\XAMPP\htdocs\mega\app\cache\dev\classes.php on line 13492 and
defined in D:\XAMPP\htdocs\mega\app\cache\dev\classes.php line 12628
I hope that my question is clear.
Thank you!
Well, I have found the simple way to solve the problem. For example, I would like to have a different Gallery for every different language of InfoPage. So, I can achieve that in this way:
# InfoPageAdmin.php
->add('translations', 'a2lix_translations', [
'fields' => [
'gallery' => [
'field_type' => 'entity',
'class' => 'AppBundle:Gallery',
],
],
])
Here, Gallery is the field of InfoPage entity:
# AppBundle/Entity/InfoPage.php
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Gallery", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="gallery_id", referencedColumnName="id")
*/
protected $gallery;
I hope that my answer help someone. :)
Edit: If you want to use 'sonata_type_model_list' in translations, working workaround is described here: https://github.com/a2lix/TranslationFormBundle/issues/155.

ManyToOne relation in sonata admin bundle

I have an entity which DishesWithCategory by the link to other entities:
/**
* #ORM\ManyToOne(targetEntity="Dishes", cascade={"persist"})
*/
protected $dishes;
/**
* #ORM\ManyToOne(targetEntity="MenuCategory", cascade={"persist"})
*/
protected $category;
In admin i have:
$formMapper
->add('dishes', 'sonata_type_admin', [
'delete' => false,
'btn_add' => false
])
->add('category', 'sonata_type_model',[
'expanded' => true,
'multiple' => true,
])
;
When I try to create a dish, I get the error:
Found entity of type Doctrine\Common\Collections\ArrayCollection on association ZaWeb\MenuBundle\Entity\DishesWithCategory#category, but expecting ZaWeb\MenuBundle\Entity\MenuCategory
Can someone faced with this? how can I fix it?
I didn't fully understand your model but i think that the issue comes from : the multiple => true on category because you can set only one category (ManyToOne) for a DishesWithCategory.
Either you need to change your model to a OneToMany for your $category or you need to remove the ArrayCollection which might be in the constructor of your model and remove multiple => true.
You might need to do :
$formMapper
->add('dishes', 'sonata_type_admin', [
'delete' => false,
'btn_add' => false
])
->add('category', 'sonata_type_model',[
'expanded' => true
])
;

Categories