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.
Related
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...
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
I got some strange situation, i working with laravel in php,
in my controllers i set "CompaniesController" and 2 more companies type controllers (Landlords & Clients).
got this error in the "ClientsController":
Access to undeclared static property: App\Http\Controllers\CRM\Pages\Companies\CompaniesController::$updates_roles
The clients & landlords controller reading the $updates_roles from CompaniesController, here is the code in the CompaniesController:
class CompaniesController extends Controller
{
/**
* Columns filters hide
*/
public static $columns_filters_hide = [
....
];
/**
* Allow Sorting Columns
*/
public static $columns_sorting = [
....
];
/**
* Query results values convert
*
* #var array
*/
public static $query_results_value = [
....
];
/**
* Updates Roles
*
* #var array
*/
public static $updates_roles = [
'Companies' =>
[
'primary' => 'id',
'fields' =>
[
'name' => [
'required' => true
],
'heb_name' => [
'required' => true
],
'website' => [],
'linkdin' => [],
'facebook' => [],
'phone' => [],
'notes' => [],
'kind' => [
'default' => 'ecosystem'
],
'rank' => ....
The problem is that all worked fine till now, and this error it's very strange becuase if i remove the file and his content, the page is still works, like it's in cache of something, the file now is uneditable, the var is not exists and i realy stuck here.
Thanks for help!
Problem solved!
CompaniesController already was exists in other namespace and the composer was built dump-autoload on the old path instead.
i removed the file from the old location and run composer dump-autoload and the problem solved.
I have a Model:
class SearchOfferFacet
{
/**
* #var string
*/
protected $id;
/**
* #var array
*/
protected $country;
//getters ans setters....
And I have a FormType
...
$builder
->add('country', ChoiceType::class, array(
'expanded' => true,
'multiple' => false,
'choices_as_values' => true,
'choices' => array('US' => "USA", "IT" => "Italia"),
))
When I create the form in my controller with
$searchOfferFacet = new SearchOfferFacet();
$searchOfferFacet->setCountry(
array(
'US' => 'US',
'MV' => 'MV'
)
);
$form = $this->createForm(SearchOfferFacetType::class, $searchOfferFacet, array('method' => 'POST'));
I get the following Exception:
An exception has been thrown during the rendering of a template ("Notice: Array to string conversion") in form_div_layout.html.twig at line 13.
But when I change the variable in my model to String, and I set a string as Country, for example "US", then the exception appears:
Expected an array.
In my TWIG there is nothing special, just:
{{ form_widget(form.country) }}
Does anyone has an idea what is wrong?
Can you try setting 'multiple' => true,? My suspicion here is that you the member is an array, but since you set 'multiple' => false,, it only needs to save a string.
On the other hand, you can then also just do
/**
* #var string
*/
protected $country;
and the setCountry(...) function should only save a string.
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
])
;