Laravel Backpack select_and_order: how to make options list dependent on model being edited - laravel-backpack

I have a select_and_order field that works in my
use App\Models\Tool;
class ProjectCrudController extends CrudController
{
protected function setupCreateOperation()
{
CRUD::addField([
'name' => 'tools'
'type' => 'select_and_order',
'options' => Tool::get()->pluck('selector','id'),
]);
Now the possible tools are dependent on the type of project, so instead of returning all tools to options as above, I would like something like
CRUD::addField([
'name' => 'tools'
'type' => 'select_and_order',
'options' => Tool
::projectTypeFilter($this->crud->model->type)
->pluck('selector','id'),
]);
But the new query returns [] because - as far as I can see - $this->crud->model is not hydrated.
protected function setupUpdateOperation()
{
CRUD::addField([
'name' => 'naam',
'type' => 'select_and_order',
'options' => dump($this->crud->getModel()),
]);
}
shows
App\Models\Project {#1779 ▼
#guarded: array:1 [▶]
#fakeColumns: array:1 [▶]
#casts: array:4 [▶]
#connection: null
#table: null
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: false
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: []
#original: []
#changes: []
#classCastCache: []
#attributeCastCache: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
}
How can I have the model hydrated?

I think what you're looking for is not $this->crud->getModel() (which will get you the Eloquent model class), but $this->crud->getCurrentEntry() which will get you:
false, if the model has not been hydrate (eg. inside setupCreateOperation(), where there is no current entry to speak of);
the current entry for that Eloquent model, if present (eg. inside setupUpdateOperation();

Related

symfony form type translation_domain not working?

i have form type like:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', null, [
'translation_domain' => 'Admin.Global',
])
->add('isoCode', null, [
'translation_domain' => 'Admin.Global',
])
->add('languageCode')
->add('flag_image', FileType::class, [
'mapped' => false,
'label' => 'flag',
'constraints' => [
new Image([
'maxSize' => '1024K'
])
],
'required' => false,
'translation_domain' => 'Admin.Global',
])
->add('active', CheckboxType::class, [
'label_attr' => [
'class' => 'switch-custom'
]
])
->add('isDefault', CheckboxType::class, [
'label_attr' => [
'class' => 'switch-custom'
]
]);
}
after i run command:
php bin/console translation:update --force en(or zh_CN)
only 'name' label in Admin.Global+intl-icu.en(or zh_CN).xlf file
why the isocode and flag not on xlf file?
and do i add 'translation_domain' => 'Admin.Global', to each formtype?
The problem is that the translation:update command, according to the docs,
extracts strings to translate from 2 locations only:
templates
any PHP file/class that injects or autowires the translator service and makes calls to the trans() method.
Your form type class is neither of these two, so it is not processed.
You mentioned that the name label gets into xlf file, while other labels do not. Obviously, name got there from some other place (from a template, for example), not from the form class.
To process form type classes you can install JMSTranslationBundle.
It provides a different command, translation:extract, which among other things supports extracting messages from (a quote from docs):
all form labels that are defined as options to the ->add() method of
the FormBuilder

How to set default selected option for Entity Type select?

so I'm trying to set a selected option in my form but I can't seem to find out how to do this. I've Googled around and everything seems to be for Symfony2 where default was a thing, this seems to be no longer the case for Symfony4.
I've tried using data and empty_data but both don't select the correct value..
# weirdly, setting to ['guru'] gets undefined index error,
# setting to $options doesn't error
->add('guru', EntityType::class, array(
'class' => User::class,
'choice_label' => 'username',
'data' => $options['guru']
))
and how I pass $options:
$form = $this->createForm(EditCategoryType::class, array('guru' => $guruName));
So with the help of #Juan I. Morales Pestana I found an answer, the only reason I've added the below as an answer rather than marking his as correct was because there seems to be a slight difference in how it works now..:
Controller now reads (Thanks to #Juan):
$category = $this->getDoctrine()->getRepository(Category::class)->find($id);
$category->setGuru($category->getGuru());
$form = $this->createForm(EditCategoryType::class, $category);
My EditCategoryType class:
->add('guru', EntityType::class, array(
'class' => User::class,
'choice_label' => 'username',
'mapped' => false,
'data' => $options['data']->getGuru()
))
updated twig template:
{{ form_widget(form.guru, { 'attr': {'class': 'form-control'} }) }}
<a href="{{ path('register_new', { idpackage: p.id }) }}" class="btn_packages">
//sends to form type the package id
$fromPackage = '';
if($request->query->get('idpackage')) {
$fromPackage = $request->query->get('idpackage');
}
$form = $this->createForm(RegisterFormType::class, $register, ['fromPackage' => $fromPackage]);
in formType set the param in options:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Register::class,
'fromPackage' => null
]);
}
->add('package',EntityType::class, [
'label' => 'Select one item',
'class' => Package::class,
'choice_label' => function($package) {
return $package->getTitle() . ' | crédits : ' . $package->getAmount();
},
'attr' => ['class' => 'input-text', ],
'placeholder' => '...',
'required' => false,
'choice_attr' => function($package) use ($idFromPackage) {
$selected = false;
if($package->getId() == $idFromPackage) {
$selected = true;
}
return ['selected' => $selected];
},
])
$parentCategory = $categoryRepository->repository->find(2);
$category = new Category();
$category->setParentCategory(parentCategory);
$form = $this->createForm(CategoryType::class, $category);
we chose the predefined top category, it works if you use it this way.
try this :
empty_data documentation
As I said in my comments you are doing something wrong. I will explain all the process:
calling the form in the controller
$person = new Person();
$person->setName('My default name');
$form = $this->createForm('AppBundle\Form\PersonType', $person);
$form->handleRequest($request);
then the createForm function is executed here is the code
Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait
protected function createForm($type, $data = null, array $options = array())
{
return $this->container->get('form.factory')->create($type, $data, $options);
}
As you can see the data or options are nos setted directly in the form, another function is called and then the form is created
This is the result when I make a dump inside the PersonType
PersonType.php on line 20:
array:35 [▼
"block_name" => null
"disabled" => false
"label" => null
"label_format" => null
"translation_domain" => null
"auto_initialize" => true
"trim" => true
"required" => true
"property_path" => null
"mapped" => true
"by_reference" => true
"inherit_data" => false
"compound" => true
"method" => "POST"
"action" => ""
"post_max_size_message" => "The uploaded file was too large. Please try to upload a smaller file."
"error_mapping" => []
"invalid_message" => "This value is not valid."
"invalid_message_parameters" => []
"allow_extra_fields" => false
"extra_fields_message" => "This form should not contain extra fields."
"csrf_protection" => true
"csrf_field_name" => "_token"
"csrf_message" => "The CSRF token is invalid. Please try to resubmit the form."
"csrf_token_manager" => CsrfTokenManager {#457 ▶}
"csrf_token_id" => null
"attr" => []
"data_class" => "AppBundle\Entity\Person"
"empty_data" => Closure {#480 ▶}
"error_bubbling" => true
"label_attr" => []
"upload_max_size_message" => Closure {#478 ▶}
"validation_groups" => null
"constraints" => []
"data" => Person {#407 ▼ // Here is the data!!!
-id: null
-name: "My default name"
-salary: null
-country: null
}
]
As you can see the data is indexed at data so that is the reason why you get an undefined index error
So the proper way is to set the entity value from the controller or use your form as a service and call the repository and set the value using the data option which has not changed since version 2. My point is that you are using the form wrong.
Please change your code.
Hope it help
EDITED IN THE SYMFONY 4 WAY(with out namespaces bundles)
Let's see please, I have a Person Entity with a name just for this example
The controller
$person = new Person(); //or $personsRepository->find('id from request')
$person->setName('My default name');
$form = $this->createForm(PersonType::class, $person);
$form->handleRequest($request);
The form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name',TextType::class,[
//'data' => 'aaaaa' // <= this works too
]);
}
The options array value which proves that you are accessing wrong
PersonType.php on line 20:
array:35 [▼
"block_name" => null
"disabled" => false
"label" => null
"label_format" => null
"translation_domain" => null
"auto_initialize" => true
"trim" => true
"required" => true
"property_path" => null
"mapped" => true
"by_reference" => true
"inherit_data" => false
"compound" => true
"method" => "POST"
"action" => ""
"post_max_size_message" => "The uploaded file was too large. Please try to upload a smaller file."
"error_mapping" => []
"invalid_message" => "This value is not valid."
"invalid_message_parameters" => []
"allow_extra_fields" => false
"extra_fields_message" => "This form should not contain extra fields."
"csrf_protection" => true
"csrf_field_name" => "_token"
"csrf_message" => "The CSRF token is invalid. Please try to resubmit the form."
"csrf_token_manager" => CsrfTokenManager {#457 ▶}
"csrf_token_id" => null
"attr" => []
"empty_data" => Closure {#480 ▶}
"error_bubbling" => true
"label_attr" => []
"upload_max_size_message" => Closure {#478 ▶}
"validation_groups" => null
"constraints" => []
"data" => Person {#407 ▼
-id: null
-name: "My default name" //Here is the data
-salary: null
-country: null
}
]
AppBundle is just another folder in my folder structure. Be pleased to test your self. The second parameter to the formCreate function is the entity or data not the options you are thinking that the options is the data.

Entities passed to the choice field must be managed - different on app.php and on app_dev.php

Welcome!
I've added a field to a form
->add('languageLevel', ChoiceType::class, [
'choices' => [
Meme::LEVEL_BEGINNER => Meme::LEVEL_BEGINNER,
Meme::LEVEL_INTERMEDIATE => Meme::LEVEL_INTERMEDIATE,
Meme::LEVEL_ADVANCED => Meme::LEVEL_ADVANCED,
Meme::LEVEL_EXPERT => Meme::LEVEL_EXPERT
]
])
This form saves entity 'Meme' with the same property
/**
* #var string
* #ORM\Column(name="language_level", type="string", length=20)
*/
private $languageLevel;
The prod.log gives me this:
[2017-12-17 23:41:18] request.INFO: Matched route "meme_add". {"route":"meme_add","route_parameters":{"_controller":"AppBundle\\Controller\\MemeController::addAction","_route":"meme_add"},"request_uri":"http://keenweasel.com/meme/add","method":"GET"} []
[2017-12-17 23:41:18] security.DEBUG: Read existing security token from the session. {"key":"_security_main"} []
[2017-12-17 23:41:18] security.DEBUG: User was reloaded from a user provider. {"username":"acid","provider":"FOS\\UserBundle\\Security\\UserProvider"} []
[2017-12-17 23:41:18] php.WARNING: Warning: spl_object_hash() expects parameter 1 to be object, integer given {"exception":"[object] (ErrorException(code: 0): Warning: spl_object_hash() expects parameter 1 to be object, integer given at /var/www/keen/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1239)"} []
[2017-12-17 23:41:18] php.WARNING: Warning: spl_object_hash() expects parameter 1 to be object, integer given {"exception":"[object] (ErrorException(code: 0): Warning: spl_object_hash() expects parameter 1 to be object, integer given at /var/www/keen/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1592)"} []
[2017-12-17 23:41:18] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Form\Exception\RuntimeException: "Entities passed to the choice field must be managed. Maybe persist them in the entity manager?" at /var/www/keen/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php line 98 {"exception":"[object] (Symfony\\Component\\Form\\Exception\\RuntimeException(code: 0): Entities passed to the choice field must be managed. Maybe persist them in the entity manager? at /var/www/keen/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php:98)"} []
[2017-12-17 23:41:18] security.DEBUG: Stored the security token in the session. {"key":"_security_main"} []
in dev.log on my dev machine there is nothing like that, and it works fine. On prod I get error 500. Any help please? I tried removing vendor and installing it one more time on both dev and prod servers. Clearing cache multiple time with --env=prod.
EDIT
Ok, got the same problem on dev server (just switched from app_dev.php to app.php in apache config and added app/autoload.php)
/**
* #Route("/meme/add", name="meme_add")
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
* #internal param Request $request
*/
public function addAction(Request $request)
{
$user = $this->getUser();
if (!$user || !$user->hasRole('ROLE_MEME_ADD_AWAITING')) {
$this->addFlash('notice', 'You need to login');
return $this->redirectToRoute('fos_user_security_login');
}
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw $this->createAccessDeniedException();
}
$meme = new Meme();
$form = $this->createForm(MemeAddType::class, $meme);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid() ) {
if ($user->hasRole('ROLE_MEME_ADD_AWAITING')) {
$meme->setPublishingStatus('awaiting-publishing');
} else if ($this->getUser()->hasRole('ROLE_MEME_ADD_MAIN')) {
$meme->setPublishingStatus('published');
}
$em = $this->getDoctrine()->getManager();
$em->persist($meme);
$em->flush();
}
return $this->render(
'#App/memes/add.html.twig',
[
'memeAddForm' => $form->createView()
]
);
}
The form:
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Language;
use AppBundle\Entity\Meme;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichImageType;
class MemeAddType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('textOne', TextareaType::class, [
'label' => 'A phrase or a word',
'attr' => ['style' => 'width:320px; height: 110px']
])
->add('textOneLang', EntityType::class , [
'class' => Language::class,
'choice_label' => 'language',
'expanded' => false,
'multiple' => false,
'label' => 'Language of the phrase of the word',
'data' => 2
])
->add('textTwo', TextareaType::class, [
'label' => 'Translation, might be with an example',
'attr' => ['style' => 'width:320px; height: 110px']
])
->add('textTwoLang', EntityType::class , [
'class' => Language::class,
'choice_label' => 'language',
'expanded' => false,
'multiple' => false,
'label' => 'Language of the translation of the phrase of the word',
'data' => 2
])
->add('languageLevel', ChoiceType::class, [
'choices' => [
Meme::LEVEL_BEGINNER => Meme::LEVEL_BEGINNER,
Meme::LEVEL_INTERMEDIATE => Meme::LEVEL_INTERMEDIATE,
Meme::LEVEL_ADVANCED => Meme::LEVEL_ADVANCED,
Meme::LEVEL_EXPERT => Meme::LEVEL_EXPERT
]
])
->add('imageFile', VichImageType::class)
->add(
'save',
SubmitType::class,
[
'attr' => ['class' => 'save']
]
)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Meme::class
]);
}
public function getBlockPrefix()
{
return 'app_bundle_meme_add';
}
public function getName() {
return 'app_bundle_meme_add';
}
}
Try this in your formType
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Language;
use AppBundle\Entity\Meme;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichImageType;
class MemeAddType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('textOne', TextareaType::class, [
'label' => 'A phrase or a word',
'attr' => ['style' => 'width:320px; height: 110px']
])
->add('textOneLang', EntityType::class , [
'class' => Language::class,
'choice_label' => 'language',
'expanded' => false,
'multiple' => false,
'label' => 'Language of the phrase of the word',
'data' => 2
])
->add('textTwo', TextareaType::class, [
'label' => 'Translation, might be with an example',
'attr' => ['style' => 'width:320px; height: 110px']
])
->add('textTwoLang', EntityType::class , [
'class' => Language::class,
'choice_label' => 'language',
'expanded' => false,
'multiple' => false,
'label' => 'Language of the translation of the phrase of the word',
'data' => 2
])
->add('languageLevel', ChoiceType::class, [
'choices' => [
Meme::LEVEL_BEGINNER => Meme::LEVEL_BEGINNER,
Meme::LEVEL_INTERMEDIATE => Meme::LEVEL_INTERMEDIATE,
Meme::LEVEL_ADVANCED => Meme::LEVEL_ADVANCED,
Meme::LEVEL_EXPERT => Meme::LEVEL_EXPERT
]
])
->add('imageFile', VichImageType::class)
->add(
'save',
SubmitType::class,
[
'attr' => ['class' => 'save']
]
)
;
$builder->get('languageLevel')->resetViewTransformers();
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Meme::class
]);
}
public function getBlockPrefix()
{
return 'app_bundle_meme_add';
}
}
Actually, what was going on was that I had
'data' => 2
in the textOneLang form element, so it was failing when it was given to spl_object_hash() function which only accepts objects, not integers. Data transformer was not used for that. But this 'data' parameter wasn't supposed to be there in the first place, don't know how it ended up there. Probably, when trying to debug it. Lesson learned, have all the options figured out and also, just dump what you have as parameter in spl_object_hash().
The difference between app_dev.php and app.php didn't have anything to do with it. It was probably cached somehow in app_dev.php and worked on some older version.

yii2 detailview conditional row class

I would like to change class for one single attribute in detailview, based on a condition:
If I wouldn't want to make it conditional, it would be working like so:
[
'attribute' => 'ungueltig',
'format' => 'boolean',
'contentOptions' => [
'class' => 'danger',
]
],
I want this one to change to conditional, and I have tried a lot of different ways, e.g.:
[
'attribute' => 'ungueltig',
'format' => 'boolean',
'contentOptions' => function ($model) {
if ($model->ungueltig == 1) {
return ['class' => 'danger'];
} else {
return '';
}
},
],
(I would think this is the most logical solution, but nothing happens, so page is loading fine but without class danger at the attribute, no error message)
or
[
'attribute' => 'ungueltig',
'format' => 'boolean',
'contentOptions' => ['class' => function ($model) {
if ($model->ungueltig == 1) {
return 'danger';
} else {
return '';
}
},]
],
= error message: htmlspecialchars() expects parameter 1 to be string, object given
so I have no clue and I don't even find any help on the web. Can you please point me to the right direction? Many thanks!
You should simply try :
'contentOptions' => [
'class' => ($model->ungueltig == 1) ? 'danger' : '',
],
DetailView display only one model, you don't need any function here.

Symfony Entity form type across OneToMany relationship

I have a entity Event which is related to another entity Parameter by a third entity EventsParameters.
Parameter contain all possible parameters for each Event. Let say "isSomething" and "isSomethingElse"
In my event create form I want to set "isSomething" and "isSomethingElse" to true or false
So I have tried to add an entity field in my form:
->add('parameters', EntityType::class, array(
'class' => 'AppBundle:Parameter' ,
'multiple' => true , ))
But I'm not sure about which class I have to set. Parameter or EventsParameters? I can't get started!
Ok I just had this line in my form builder
->add('enventsParameters' ,EntityType::class , array(
'class' => 'AppBundle:Parameter' ,
'choice_label' => function ($obj) {
return $obj->getParameterName();
},
'expanded' => true ,
'multiple' => true , ))