Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Installation and First Admin

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Hello! And welcome to another amazing episode of: "Recipes with Ryan". Today we'll be baking a positively mind-blowing, mile-high cinnamon bread!

Huh? It's not the recipe video? EasyAdminBundle? Ok, cool!

Um... we'll be baking a positively mind-blowing admin interface in Symfony with the wonderful EasyAdminBundle.

What about SonataAdminBundle

Wait, but what about SonataAdminBundle? SonataAdminBundle is super powerful... more powerful than EasyAdminBundle. But it's also a bit of a beast - a lot bigger and more difficult to use. If it has a feature that you need... go for it! Otherwise, jump into the easy side with EasyAdminBundle.

Code with Me!

To make your cinnamon bread a hit, code along with me. Download the course code from this page and unzip it. Inside, you'll find a directory called start/, which will have the same code you see here. Open README.md for step-by-step baking instructions... and also details on how to get your project setup!

The last step will be to find your favorite terminal, move into the project and run:

php bin/console server:run

to start the built-in web server. Find your fanciest browser and open that: http://localhost:8000. Welcome to AquaNote! The project we've been building in our Symfony series.

Actually, in that series, we spent some serious time making an admin area for one of our entities: Genus. Go to /admin/genus... and then login: username weaverryan+1@gmail.com, password: iliketurtles.

A genus is a type of animal classification. And after all our work, we can create and edit them really nicely.

That's great... but now I need an admin interface for a bunch of other entities: GenusNote, SubFamily, and User. Doing that by hand... well... that would take a while... and we've got baking to do! So instead, we'll turn to EasyAdminBundle.

Installing EasyAdminBundle

Google for EasyAdminBundle and find its GitHub page. Ah, it's made by our friend Javier! Hi Javier! Let's get this thing installed. Copy the composer require line, go back to your terminal, open a new tab, and paste:

composer require javiereguiluz/easyadmin-bundle

While Jordi is downloading that package, let's keep busy!

Copy the new bundle line, find app/AppKernel.php, and put it here!

... lines 1 - 2
use JavierEguiluz\Bundle\EasyAdminBundle\EasyAdminBundle;
... lines 4 - 6
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
... lines 12 - 25
new EasyAdminBundle(),
);
... lines 28 - 38
}
... lines 40 - 59
}

Unlike most bundles, this bundle actually gives us a new route, which we need to import. Copy the routing import lines, find our app/config/routing.yml and paste anywhere:

... lines 1 - 9
easy_admin_bundle:
resource: "@EasyAdminBundle/Controller/"
type: annotation
prefix: /easyadmin

Since we already have some pages that live under /admin, let's change the prefix to /easyadmin:

... lines 1 - 9
easy_admin_bundle:
... lines 11 - 12
prefix: /easyadmin

Finally, one last step: run the assets:install command. This should run automatically after Composer is finished... but just in case, run it again:

php bin/console assets:install --symlink

This bundle comes with some CSS and JavaScript, and we need to make sure it's available.

Setting up your First Admin

Ok, we are installed! So... what did we get for our efforts? Try going to /easyadmin. Well... it's not much to look at yet... but it will be! I promise! We just need to configure what admin sections we need... and Javier gave us a great error message about this!

In a nut shell, EasyAdminBundle can create an admin CRUD for any entity with almost zero configuration. In the docs, it shows an example of this minimal config. Copy that, find config.yml, scroll to the bottom, and paste. Change these to our entity names: AppBundle\Entity\Genus, AppBundle\Entity\GenusNote, AppBundle\Entity\SubFamily, skip GenusScientist - we'll embed that inside one of the other forms, and add AppBundle\Entity\User:

... lines 1 - 80
easy_admin:
entities:
- AppBundle\Entity\Genus
- AppBundle\Entity\GenusNote
- AppBundle\Entity\SubFamily
- AppBundle\Entity\User

We have arrived... at the moment of truth. Head back to your browser and refresh Ah, hah! Yes! A full CRUD for each entity: edit, delete, add, list, show, search, party! For a wow factor, it's even responsive: if you pop it into iPhone view, it looks pretty darn good!

This is exactly what I want for my admin interface: I want it to be amazing and I want it to let me be lazy!

Of course the trick with this bundle is learning to configure and extend it. We're 80% of the way there with no config... now let's go further.

Leave a comment!

53
Login or Register to join the conversation
Nick-F Avatar

RIP the mobile friendly easyadmin
Why in the world javier thought removing responsive design was a good idea, I will never understand

Reply

Hey Nfleming65

I hope you will understand, see this comment and the reference to another issue in it for more information about purposes behind this change:
https://github.com/EasyCorp...

Cheers!

Reply
Brent Avatar
Brent Avatar Brent | posted 3 years ago | edited

I am trying to use multiple entity managers. The first one in my doctrine.yml is for my User authenticator, second is for querying invoices. It seems as though setting the entity manager per entity is supported (<a href="https://github.com/EasyCorp/EasyAdminBundle/pull/99&quot;&gt;#99&lt;/a&gt;) but I haven't figure out how? I've tried the following config but I continue to get a SQL because the User entity manager and the invoices table doesn't exist there:

my easy_admin.yaml
`
entities:

    # List the entity class name you want to manage
    InvoiceHeader:
        class: App\Entity\InvoiceHeader
        entity_manager: 'Main'

list:

entities:
    # List the entity class name you want to manage
    InvoiceHeader:
        class: App\Entity\InvoiceHeader
        entity_manager: '@doctrine.orm.Main_entity_manager'
list:
entities:
    # List the entity class name you want to manage
    InvoiceHeader:
        class: App\Entity\InvoiceHeader
        entity_manager: 'doctrine.orm.Main_entity_manager'

`

my doctrine.yaml
`
doctrine:

dbal:
    default_connection: Main
    # configure these for your database server
    connections:
        User:
            driver: 'pdo_mysql'
            server_version: '5.7'
            charset: utf8mb4
            default_table_options:
                charset: utf8mb4
                collate: utf8mb4_unicode_ci

            url: '%env(DATABASE_USER_URL)%'

        Main:
            driver: 'pdo_mysql'
            server_version: '5.7'
            charset: utf8mb4
            default_table_options:
                charset: utf8mb4
                collate: utf8mb4_unicode_ci

            url: '%env(resolve:MYSQL_DATABASE_URL)%'
orm:
    auto_generate_proxy_classes: true
    default_entity_manager: Main
    entity_managers:
        User:
            connection: User
            naming_strategy: doctrine.orm.naming_strategy.underscore
            mappings:
                User:
                    is_bundle: false
                    type: annotation
                    dir: '%kernel.project_dir%/src/Entity'
                    prefix: 'App\Entity'
                    alias: User

        Main:
            connection: Main
            naming_strategy: doctrine.orm.naming_strategy.underscore
            mappings:
                Main:
                    is_bundle: false
                    type: annotation
                    dir: '%kernel.project_dir%/src/Entity'
                    prefix: 'App\Entity'
                    alias: Main

`

Reply

Hi!

What I understood from that PR is you don't have to do anything else than configuring correctly your entity managers and connections, and it should work out of the box. At a first sight to your config code I can see that the dir option of your managers is wrong. Your entities should be isolated into their own folder. I recommend you to read this piece of documentation https://symfony.com/doc/current/doctrine/multiple_entity_managers.html but if you have more questions feel free to ask us :)

Cheers!

Reply
Brent Avatar
Brent Avatar Brent | MolloKhan | posted 3 years ago | edited

I moved my entities into the same folder and changed my dir and prefix to look like this. Thank you!

`

dir: '%kernel.project_dir%/src/Entity/User'

prefix: 'App\Entity\User'

dir: '%kernel.project_dir%/src/Entity/InvoiceHeader'

prefix: 'App\Entity\InvoiceHeader'

`

Reply
Stefan D. Avatar
Stefan D. Avatar Stefan D. | posted 4 years ago

What happened to the design, I now have a some strange grey design.

Reply

Hey Stefan D.

Can you send us a screenshot to hello@symfonycasts.com?

Thanks!

1 Reply

Ohh, you should be on EasyAdminBundle@2 (new version). This tutorial is based on version 1. A lot of things should keep working but mostly you should see changes to the design. If you want you can check this file to get a better idea of what changed: https://github.com/EasyCorp...

Cheers!

Reply
Shaun T. Avatar
Shaun T. Avatar Shaun T. | posted 4 years ago

Is it possible to configure access to Easy Admin Bundle such that different users to get to see different views?

For example a manager can only see the staff for the their own office, but a company owner can see all managers, all offices, and other entities also.

Reply

Hey Shaun,

Good question! I've never done this before, but it seems doable. But you probably need to override initial controllers/templates to implement it. I can advise you to look at this docs: https://symfony.com/doc/mas... . So, basically, you can override a lot and add your own custom logic. Btw, we also covered how to override different parts of this bundle in this screencast, see:
https://symfonycasts.com/sc...
https://symfonycasts.com/sc...
https://symfonycasts.com/sc...
etc.

So, in short, I think it's possible, but it requires more custom work and overrides from you.

However, if you really need a flexible system, you can take a look at SonataAdminBundle - it's more complex, but also more flexible for configuring access.

Cheers!

Reply
Apr Avatar

Hello friends from Knp,

I'm using the EasyAdminBundle as it follows in the tutorial and in the table of Users when I try to update the email (this field has to be unique obviously) I get this next error if I try to update the user 1 with the email from another user.

I also saw in this link from symfony how to integrate FOSUserBundle with EasyAdmin (https://symfony.com/doc/mas... and I added this next controller but nothing changes apparently.

// src/AppBundle/Controller/AdminController.php
namespace AppBundle\Controller;

use EasyCorp\Bundle\EasyAdminBundle\Controller\AdminController as BaseAdminController;

class EasyAdminController extends BaseAdminController
{
public function createNewUserEntity()
{
return $this->get('fos_user.user_manager')->createUser();
}

public function persistUserEntity($user)
{
$this->get('fos_user.user_manager')->updateUser($user, false);
parent::persistEntity($user);
}

public function updateUserEntity($user)
{
$this->get('fos_user.user_manager')->updateUser($user, false);
parent::updateEntity($user);
}
}

I was expecting the validation showing the message that this email is already used or something like this. I suppose that can be managed somehow with the config file perhaps? What solution I should take?

Error:

An exception
occurred while executing 'UPDATE `user` SET email = ?, email_canonical =
? WHERE id = ?' with params ["emailfromanotheruser@email.example",
"
emailfromanotheruser@email.example
", 8]:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '
emailfromanotheruser@email.example
' for key 'UNIQ_8D93Ddf9f0s96FBF'

Reply
Apr Avatar

Horaaay! I got it! Last part of this link. https://symfony.com/doc/mas...

Adding this line in the config file inside the "form" label in my case.

easyadmin:
->
entities:
->
User:
->
form:
->
form_options: { validation_groups: ['Profile'] }

Reply
MolloKhan Avatar MolloKhan | SFCASTS | Apr | posted 4 years ago | edited

Hey Apr
That's nice, congrats for solving your problem!

FYI EasyAdminBundle doesn't do anything special about validations, it just uses Symfony's validation system and almost everything can be customizable via config

Cheers!

Reply
Default user avatar

Hi guys,
For some reasons the easyadmin-all.css & easyadmin-all.js files a not loaded. When i check in my browser's console they both have a 404. When i open the source code of the page and try to click both css & js links it opens them normally. Also, i don't understand why i literraly have no /web/bundles/easyadmin folder with those 2 files. Using the assets:install cmd gives me this msg : " [OK] All assets were successfully installed. "

So right now i'm stuck with a pretty ugly interface... and i have no clue how to make the easyadminbundle assets install and work properly.

Any ideas ?

Reply
Default user avatar

Of course it works if i do it by hand : create the /web/bundles/easyadmin/ : /stylesheet , /javascript & /fonts folders and put the files there by hand (taken from the vendor /ressources/public folders of the bundle). But i want to make it work the right way.

Reply
Victor Avatar Victor | SFCASTS | Antsen | posted 5 years ago | edited

Hey Antsen,

That's really weird because installing assets had to do the trick. Could you clear the cache first, and then try to install assets again? Btw, what OS are you on? try to do install assets by create a hard copy of them, not symlink them:


$ rm -rf var/cache/*
$ bin/console assets:install

Does it fix the problem?

Cheers!

Reply
Default user avatar

Hi Victor,
I'm under Windows7. I had already tried clearing the cache and re running the install assets command (with symlink & a hard copy) but it just won't work. Thanks for your reply anyways. Since i'm running out of time i'll just do it by hand.

Reply

Hey Antsen,

That's weird. Please, also make sure you register this bundle in AppKernel.php before doing those commands. Looks like your application just do not see this bundle or you have a permissions issue, but I suppose the command should fail in this case.

Cheers!

Reply
Default user avatar
Default user avatar Mailbox Spain Mail forwarding | posted 5 years ago

I am using symfony 3.3 and i am getting error when i try to edit any rows:
Catchable Fatal Error: Object of class AppBundle\Entity\GenusScientist could not be converted to string

Exception Logs 1 Stack Trace
Symfony\Component\Debug\Exception\
ContextErrorException
in vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php (line 59)
DoctrineType::createChoiceLabel(object(GenusNote), 0, '1')
call_user_func(array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), object(GenusNote), 0, '1')
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php (line 130)
DefaultChoiceListFactory::addChoiceView(object(GenusNote), '1', array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), null, array(), array(), array())
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php (line 196)
DefaultChoiceListFactory::addChoiceViewsGroupedBy(array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100'), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), array(object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote), object(GenusNote)), array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), null, array(), array(), array())
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php (line 95)
DefaultChoiceListFactory->createView(object(LazyChoiceList), array(), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), null, null)
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php (line 227)
PropertyAccessDecorator->createView(object(LazyChoiceList), array(), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), null, null)
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php (line 168)
CachingFactoryDecorator->createView(object(LazyChoiceList), array(), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), null, null)
in vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php (line 447)
ChoiceType->createChoiceListView(object(LazyChoiceList), array('block_name' => null, 'disabled' => false, 'label_format' => null, 'auto_initialize' => true, 'trim' => true, 'property_path' => null, 'mapped' => true, 'by_reference' => true, 'inherit_data' => false, 'method' => 'POST', 'action' => '', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', 'help' => null, 'error_mapping' => array(), 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), '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' => object(CsrfTokenManager), 'csrf_token_id' => null, 'expanded' => false, 'label' => null, 'attr' => array('multiple' => true, 'data-widget' => 'select2'), 'translation_domain' => 'messages', 'data_class' => null, 'multiple' => true, 'empty_data' => array(), 'required' => false, 'error_bubbling' => false, 'label_attr' => array(), 'compound' => false, 'upload_max_size_message' => object(Closure), 'validation_groups' => null, 'constraints' => array(), 'choices' => null, 'choices_as_values' => true, 'query_builder' => null, 'em' => object(DoctrineORMEntityManager_0000000010991fcd00000000739c3d57f4f8a253a9865f12c8f3d1e0f1f8846b), 'class' => 'AppBundle\\Entity\\GenusNote', 'id_reader' => object(IdReader), 'choice_loader' => object(DoctrineChoiceLoader), 'choice_label' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), 'choice_name' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), 'choice_value' => array(object(IdReader), 'getIdValue'), 'choice_attr' => null, 'preferred_choices' => array(), 'group_by' => null, 'placeholder' => null, 'choice_translation_domain' => false))
in vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php (line 193)
ChoiceType->buildView(object(FormView), object(Form), array('block_name' => null, 'disabled' => false, 'label_format' => null, 'auto_initialize' => true, 'trim' => true, 'property_path' => null, 'mapped' => true, 'by_reference' => true, 'inherit_data' => false, 'method' => 'POST', 'action' => '', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', 'help' => null, 'error_mapping' => array(), 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), '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' => object(CsrfTokenManager), 'csrf_token_id' => null, 'expanded' => false, 'label' => null, 'attr' => array('multiple' => true, 'data-widget' => 'select2'), 'translation_domain' => 'messages', 'data_class' => null, 'multiple' => true, 'empty_data' => array(), 'required' => false, 'error_bubbling' => false, 'label_attr' => array(), 'compound' => false, 'upload_max_size_message' => object(Closure), 'validation_groups' => null, 'constraints' => array(), 'choices' => null, 'choices_as_values' => true, 'query_builder' => null, 'em' => object(DoctrineORMEntityManager_0000000010991fcd00000000739c3d57f4f8a253a9865f12c8f3d1e0f1f8846b), 'class' => 'AppBundle\\Entity\\GenusNote', 'id_reader' => object(IdReader), 'choice_loader' => object(DoctrineChoiceLoader), 'choice_label' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), 'choice_name' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), 'choice_value' => array(object(IdReader), 'getIdValue'), 'choice_attr' => null, 'preferred_choices' => array(), 'group_by' => null, 'placeholder' => null, 'choice_translation_domain' => false))
in vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormType.php (line 148)
ResolvedFormType->buildView(object(FormView), object(Form), array('block_name' => null, 'disabled' => false, 'label_format' => null, 'auto_initialize' => true, 'trim' => true, 'property_path' => null, 'mapped' => true, 'by_reference' => true, 'inherit_data' => false, 'method' => 'POST', 'action' => '', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', 'help' => null, 'error_mapping' => array(), 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), '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' => object(CsrfTokenManager), 'csrf_token_id' => null, 'expanded' => false, 'label' => null, 'attr' => array('multiple' => true, 'data-widget' => 'select2'), 'translation_domain' => 'messages', 'data_class' => null, 'multiple' => true, 'empty_data' => array(), 'required' => false, 'error_bubbling' => false, 'label_attr' => array(), 'compound' => false, 'upload_max_size_message' => object(Closure), 'validation_groups' => null, 'constraints' => array(), 'choices' => null, 'choices_as_values' => true, 'query_builder' => null, 'em' => object(DoctrineORMEntityManager_0000000010991fcd00000000739c3d57f4f8a253a9865f12c8f3d1e0f1f8846b), 'class' => 'AppBundle\\Entity\\GenusNote', 'id_reader' => object(IdReader), 'choice_loader' => object(DoctrineChoiceLoader), 'choice_label' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), 'choice_name' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), 'choice_value' => array(object(IdReader), 'getIdValue'), 'choice_attr' => null, 'preferred_choices' => array(), 'group_by' => null, 'placeholder' => null, 'choice_translation_domain' => false))
in vendor/symfony/symfony/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php (line 110)
ResolvedTypeDataCollectorProxy->buildView(object(FormView), object(Form), array('block_name' => null, 'disabled' => false, 'label_format' => null, 'auto_initialize' => true, 'trim' => true, 'property_path' => null, 'mapped' => true, 'by_reference' => true, 'inherit_data' => false, 'method' => 'POST', 'action' => '', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', 'help' => null, 'error_mapping' => array(), 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), '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' => object(CsrfTokenManager), 'csrf_token_id' => null, 'expanded' => false, 'label' => null, 'attr' => array('multiple' => true, 'data-widget' => 'select2'), 'translation_domain' => 'messages', 'data_class' => null, 'multiple' => true, 'empty_data' => array(), 'required' => false, 'error_bubbling' => false, 'label_attr' => array(), 'compound' => false, 'upload_max_size_message' => object(Closure), 'validation_groups' => null, 'constraints' => array(), 'choices' => null, 'choices_as_values' => true, 'query_builder' => null, 'em' => object(DoctrineORMEntityManager_0000000010991fcd00000000739c3d57f4f8a253a9865f12c8f3d1e0f1f8846b), 'class' => 'AppBundle\\Entity\\GenusNote', 'id_reader' => object(IdReader), 'choice_loader' => object(DoctrineChoiceLoader), 'choice_label' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), 'choice_name' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), 'choice_value' => array(object(IdReader), 'getIdValue'), 'choice_attr' => null, 'preferred_choices' => array(), 'group_by' => null, 'placeholder' => null, 'choice_translation_domain' => false))
in vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormType.php (line 145)
ResolvedFormType->buildView(object(FormView), object(Form), array('block_name' => null, 'disabled' => false, 'label_format' => null, 'auto_initialize' => true, 'trim' => true, 'property_path' => null, 'mapped' => true, 'by_reference' => true, 'inherit_data' => false, 'method' => 'POST', 'action' => '', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', 'help' => null, 'error_mapping' => array(), 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), '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' => object(CsrfTokenManager), 'csrf_token_id' => null, 'expanded' => false, 'label' => null, 'attr' => array('multiple' => true, 'data-widget' => 'select2'), 'translation_domain' => 'messages', 'data_class' => null, 'multiple' => true, 'empty_data' => array(), 'required' => false, 'error_bubbling' => false, 'label_attr' => array(), 'compound' => false, 'upload_max_size_message' => object(Closure), 'validation_groups' => null, 'constraints' => array(), 'choices' => null, 'choices_as_values' => true, 'query_builder' => null, 'em' => object(DoctrineORMEntityManager_0000000010991fcd00000000739c3d57f4f8a253a9865f12c8f3d1e0f1f8846b), 'class' => 'AppBundle\\Entity\\GenusNote', 'id_reader' => object(IdReader), 'choice_loader' => object(DoctrineChoiceLoader), 'choice_label' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), 'choice_name' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), 'choice_value' => array(object(IdReader), 'getIdValue'), 'choice_attr' => null, 'preferred_choices' => array(), 'group_by' => null, 'placeholder' => null, 'choice_translation_domain' => false))
in vendor/symfony/symfony/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php (line 110)
ResolvedTypeDataCollectorProxy->buildView(object(FormView), object(Form), array('block_name' => null, 'disabled' => false, 'label_format' => null, 'auto_initialize' => true, 'trim' => true, 'property_path' => null, 'mapped' => true, 'by_reference' => true, 'inherit_data' => false, 'method' => 'POST', 'action' => '', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', 'help' => null, 'error_mapping' => array(), 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), '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' => object(CsrfTokenManager), 'csrf_token_id' => null, 'expanded' => false, 'label' => null, 'attr' => array('multiple' => true, 'data-widget' => 'select2'), 'translation_domain' => 'messages', 'data_class' => null, 'multiple' => true, 'empty_data' => array(), 'required' => false, 'error_bubbling' => false, 'label_attr' => array(), 'compound' => false, 'upload_max_size_message' => object(Closure), 'validation_groups' => null, 'constraints' => array(), 'choices' => null, 'choices_as_values' => true, 'query_builder' => null, 'em' => object(DoctrineORMEntityManager_0000000010991fcd00000000739c3d57f4f8a253a9865f12c8f3d1e0f1f8846b), 'class' => 'AppBundle\\Entity\\GenusNote', 'id_reader' => object(IdReader), 'choice_loader' => object(DoctrineChoiceLoader), 'choice_label' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceLabel'), 'choice_name' => array('Symfony\\Bridge\\Doctrine\\Form\\Type\\DoctrineType', 'createChoiceName'), 'choice_value' => array(object(IdReader), 'getIdValue'), 'choice_attr' => null, 'preferred_choices' => array(), 'group_by' => null, 'placeholder' => null, 'choice_translation_domain' => false))
in vendor/symfony/symfony/src/Symfony/Component/Form/Form.php (line 1019)
Form->createView(object(FormView))
in vendor/symfony/symfony/src/Symfony/Component/Form/Form.php (line 1022)
Form->createView()
in vendor/javiereguiluz/easyadmin-bundle/src/Controller/AdminController.php (line 221)
AdminController->editAction()
call_user_func_array(array(object(AdminController), 'editAction'), array())
in vendor/javiereguiluz/easyadmin-bundle/src/Controller/AdminController.php (line 710)
AdminController->executeDynamicMethod('edit<entityname>Action')
in vendor/javiereguiluz/easyadmin-bundle/src/Controller/AdminController.php (line 75)
AdminController->indexAction(object(Request))
call_user_func_array(array(object(AdminController), 'indexAction'), array(object(Request)))
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php (line 153)
HttpKernel->handleRaw(object(Request), 1)
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php (line 68)
HttpKernel->handle(object(Request), 1, true)
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php (line 171)
Kernel->handle(object(Request))
in web/app_dev.php (line 30)
$kernel = new AppKernel('dev', true);$kernel->loadClassCache();$request = Request::createFromGlobals();$response = $kernel->handle($request);$response->send();$kernel->terminate($request, $response);
require('/Library/WebServer/Documents/ea/web/app_dev.php')
in vendor/symfony/symfony/src/Symfony/Bundle/WebServerBundle/Resources/router.php (line 42)

Delete is not working too:
There is a ForeignKeyConstraintViolationException for the Doctrine entity associated with "Genus". Solution: disable the "delete" action for this entity or configure the "cascade={"remove"}" attribute for the related property in the Doctrine entity. Full exception: An exception occurred while executing 'DELETE FROM genus WHERE id = ?' with params [10]:

SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`easy_admin_tutorial`.`genus_note`, CONSTRAINT `FK_6478FCEC85C4074C` FOREIGN KEY (`genus_id`) REFERENCES `genus` (`id`))

Reply

Yo!

About your first error, I believe you just have to add __toString() method to GenusScientist class
And about the delete error, that's happening because you are trying to remove a row which other rows depend on, so you have two options:
1) Add cascade={"remove"} as recommended or
2) Tweak your delete action so you can manually remove the related object

Cheers!

1 Reply
Dennis E. Avatar
Dennis E. Avatar Dennis E. | posted 5 years ago

I get an error when I run assets:install. The error is: 'The target directory "public" does not exist'. My docroot is public_html and not web. Maybe this has something to do with it?

Reply

Hey Dennis E.!

How are you running the command? Try passing to it "--symlink" option, if that doesn't fix it, I believe you are correct and you would have to change your public directory to web

Cheers!

Reply
Dennis E. Avatar
Dennis E. Avatar Dennis E. | MolloKhan | posted 5 years ago | edited

Hi MolloKhan ,
Thanks for your reply.

I already tried it with the --symlink option. Same error.
Is there no other way? Because when the package is installed, it also executes that command, and it works then.

Greetings,
Dennis

Reply

Hmm, interesting, could you show me your composer.json file?

Try this: php bin/console assets:install --symlink --relative

I believe there is a way to tell Symfony about the name of your public directory, but I just can't find how to do it right now

Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 5 years ago | edited

I have Post model like this:



namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Post
 *
 * @ORM\Table(name="post")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
 */
class Post
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @ORM\ManyToMany(targetEntity="Tag", cascade={"persist"})
     * @ORM\JoinTable(name="post_tag")
     */
    private $postTags;

    public function __construct()
    {
        $this->postTags = new ArrayCollection();
    }

    public function getPostTags()
    {
        $postTagsArrayName = [];
        foreach ($this->postTags as $key => $tag) {
            array_push($postTagsName, $tag->getTagName());
        }
        return $postTagsArrayName;
    }

    public function getPostTagsName()
    {
        $postTagsName = [];
        foreach ($this->postTags as $key => $tag) {
            array_push($postTagsName, $tag->getTagName());
        }
        return implode(', ', $postTagsName);
    }

    public function addTag(Tag $tag)
    {
        $this->postTags->add($tag);
    }
}

I have also PostFormType like this, I tried to use ModelTransformer like in Symfony Docs. But transform and reverseTransform functions doesn't use Tags ArrayCollection as far as I understand, but only an array of tag names:


namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use AppBundle\Entity\Category;
use AppBundle\Repository\CategoryRepository;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\CallbackTransformer;

class PostFormType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('postTitle', TextType::class)
            ->add('postAuthor', TextType::class)
            ->add('postContent', TextareaType::class)
            ->add('postCategory', EntityType::class, [
                'placeholder' => 'Choose a Category',
                'class' => 'AppBundle:Category',
                'query_builder' => function (CategoryRepository $repo) {
                    return $repo->createAlphabeticalQueryBuilder();
                }
            ])
            ->add('postStatus', ChoiceType::class, array(
                'choices' => array(
                    'published' => 'published',
                    'not_published' => 'draft'
                ),
                'expanded' => TRUE
            ))
            ->add('postImage', FileType::class, array('data_class' => null))
            ->add('postTags', TextType::class);

        $builder
            ->get('postTags')
            ->addModelTransformer(new CallbackTransformer(
                function ($tagsAsArray) {
//transform the array to a string
                    return implode(', ', $tagsAsArray);
                },
                function ($tagsAsString) {
//transform the string back to an array
                    return explode(', ', $tagsAsString);
                }
            ));
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Post'
        ));
    }
}

Tag model is like this:


namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Tag
 *
 * @ORM\Table(name="tag")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
 */
class Tag
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     * @ORM\Column(name="tag_name", type="string", length=255)
     * @Assert\NotBlank()
     */
    private $tagName;

    public function getTagName()
    {
        return $this->tagName;
    }

    public function setTagName($tagName)
    {
        $this->tagName = $tagName;
    }
}

And finally the form twig template is like so (_form.html.twig):


{% form_theme postForm 'form/rows.html.twig' %}

{{ form_start(postForm) }}
{{ form_errors(postForm) }}

{{ form_row(postForm.postTitle, {
    'label': 'Post Name',
    'attr': {
        'placeholder': "Give this post a name",
        'class' : 'form-control'
    }

}) }}
{{ form_row(postForm.postAuthor, {
    'attr' : {
        'placeholder': "Specify the author of this post",
        'class' : 'form-control'
    }
}) }}
{{ form_row(postForm.postContent, {
    'attr' : {
        'placeholder': "Fill in some content",
        'class' : 'form-control'
    }
}) }}
{{ form_row(postForm.postCategory, {
    'attr' : {
        'class' : 'form-control'
    }
}) }}
{{ form_row(postForm.postImage) }}
{{ form_row(postForm.postStatus, {
    'label' : 'Is Published?'
}) }}
{{ form_row(postForm.postTags, {
    'attr' : {
        'placeholder': "Some tags separated by a coma. Eg: ruby, symfony",
        'class' : 'form-control'
    }
}) }}

<div class="form-group">
    <input type="submit" class="btn btn-primary" name="submit" value="Save"/>
</div>

{{ form_end(postForm) }}

What I must write on Post model for this to work? I must write on Tag model part too? Example is here (https://symfony.com/doc/cur.... If i'd use something like src/AppBundle/Form/DataTransformer/IssueToNumberTransformer.php in the second example how first example should be translated into an eventually TagsCollectionTransformer? What I should write on model part in this case? Now I tried to use first example but I miss something because when I submit the new form I obtain this error -> Could not determine access type for property "postTags" .

Reply

Hi Diaconescu!

Ok, I like your setup :). First, let's talk just about the error. Because you have a field called "postTags", the form system is looking for a setPostTags() method to call when your form submits. The error basically is telling you that it can't figure out how to set the data back onto your Post object. So, add a setPostTags method.

Now, to actually get this working! Basically, you're close... and you do want to use a data transformer. The problem is that you're starting with an ArrayCollection of Post object. So, the first part of your transformer would look more like this:


        function (ArrayCollection $tagsAsArray) {
            // transform the ArrayCollection to a string
            $tagStrings = array_map(function(Tag $tag) {
                return $tag->getTagName()
            }, $tagsAsArray->toArray());

            return implode(', ', $tagStrings);
        },

But, the other side of the transformer is a bit more complex: you need to transform a CSV of tags into an ArrayCollection of Tag objects. This means you need to (A) use explode to get an array (B) query for Tag objects for each tag string and (C) put the final thing into a new ArrayCollection($tagObjects) object. This is the example you want to follow in the docs: https://symfony.com/doc/current/form/data_transformers.html#harder-example-transforming-an-issue-number-into-an-issue-entity

Let me know if that helps!

Cheers!

Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 5 years ago

Most probably controller isn't ok too:
/**
* @Route("/post/new", name="admin_new_post")
*
*/
public function newAction(Request $request){
$post = new Post();
$form = $this->createForm(PostFormType::class, $post);

// only handles data on POST
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$post = $form->getData();
$em = $this->getDoctrine()->getManager();

$em->persist($post);
$em->flush();
$this->addFlash('success', 'Post created');

return $this->redirectToRoute('admin_list_posts');
} else if ($form->isSubmitted() && !$form->isValid()){
$this->addFlash('failed', "fail to create post");
}

return $this->render(
'admin/post/new.html.twig',
array(
'postForm' => $form->createView()
)
);
}

Reply

At first glance, this controller looks ok to me! But, why do you need a custom controller at all? You should be able to allow EasyAdminBundle to do all this work for you :).

Cheers!

Reply
Kaizoku Avatar
Kaizoku Avatar Kaizoku | posted 5 years ago

Is it possible to specify only this bundle for the command

php bin/console assets:install --symlink

I don't wan't all my other bundle to have symlinks into /web as I manage my assets with gulp.

Reply

Yo Kaizoku!

Unfortunately, assets:install always does *all* bundles, you can't select only some. But, it's no big deal - you could *not* run assets:install, and instead, create the symlink manually :). The task doesn't do anything special.

Cheers!

Reply
Kaizoku Avatar

Thanks Ryan !
As you said, no big deal :)

Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 5 years ago

I want to render in some template a controller that lies under Admin subdirectory in Controller directory
The name of the controller is CategoryAdminController.
How to write this?
{{ render(controller("AppBundle:Admin/CategoryAdmin:new")) }} is not correct but I don't find valid reference for this situation and I don't know how to correct this sintax

Reply

Hey Diaconescu!

Ah, you're close! It should be:


{{ render(controller('AppBundle:Admin\CategoryAdmin:new')) }}

It's just a change from / to \ (you may need \\ instead of a single \ due to escaping problems, I can;'t remember. If a single \ doesn't work, try 2). This is because you're really pointing to the namespace of this class, not the subdirectory. Alternatively, you can always use the "long" format:


{{ render(controller('AppBundle\Controller\Admin\CategoryAdminController::newAction')) }}

This is actually what the first string "expands" into internally. Symfony really wants the format ControllerClassName::methodName.

Cheers!

Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 5 years ago | edited

I didn't delve into EasyBundle Admin yet. Currently I try to make this application to work. I mantained the transform part in forem as before but I override __toString() method of Tag model return $this->getTagName(); and in Post model I write


public function getPostTags(){
    return $this->postTags->toArray();
  }

before you manage to respond . With these alterations in place transform part is back in business.

For reverse I wrote :


function ($tagsAsString){
          $tagNamesArray = explode(', ', $tagsAsString);
          $tagsArrayCollection = new ArrayCollection();
          
          foreach($tagNamesArray as $key => $tagName){
            $tag = $this->em->getRepository('AppBundle:Tag')->findOneBy(['tagName' => $tagName]);
            
            if (!$tag){
              $tag = new Tag();
              $tag->setTagName($tagName);
              $this->em->persist($tag);
              $this->em->flush();
            }
            
            $tagsArrayCollection->add($tag);
          }
          
          return $tagsArrayCollection;
        }

In Post model I wrote:


public function setPostTags($tags){
    foreach ($tags->toArray() as $key => $tag){
      if ($this->postTags->contains($tag)){
        continue;
      }
      $this->addTag($tag);
    }
    
    return $this;
  }

I made a service for PostFormType of course.

These setups works great for new Posts. I'm not so sure how to write this method for edit part. If some post have some tags initially and remove them in form I wish to reflect in database these changes. Duplication should be resolved with these code. I think to begin with an empty slate of tags array collection at the beginning of setPostTagsMethod and fill it only with tags provided in the form. For everything else probably I should invent some removeTag method or so to remove the undesired tags. How would you write this method?

Reply

Hey Diaconescu!

Ah, nice work! Ok, a few things:

A) In Post::getPostTags() you wrote return $this->postTags->toArray(); This might be fine... but just be careful with this. Usually, when I call a getter method like this, I expect the pure value - the ArrayCollection. I don't expect it to be transformed to an array. So, I think you'll be fine, but I would prefer if you just returned $this->postTags and then updated your data transformer to handle the ArrayCollection there.

B) About how to remove tags when they are removed from an edit Post form. Yes, I would simply clear the tags before adding them. So:


public function setPostTags($tags){
    // start with this empty
    $this->postTags->clear();

    foreach ($tags->toArray() as $key => $tag){
      if ($this->postTags->contains($tag)){
        continue;
      }
      $this->addTag($tag);
    }
    
    return $this;
  }

Because this is a ManyToMany relationships, Doctrine will automatically see that some tags are now removed from postTags and it should delete those rows :).

Cheers!

Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 5 years ago

I modified some fields in Tag and Post model to:
/**
* @ORM\ManyToMany(targetEntity="Post", mappedBy="postTags", cascade={"persist"}, orphanRemoval=true)
* @ORM\JoinTable(name="post_tag")
*/
private $tagPosts;

/**
* @ORM\ManyToMany(targetEntity="Tag", inversedBy="tagPosts", fetch="EXTRA_LAZY")
* @ORM\JoinTable(name="post_tag")
*/
private $postTags;

I know I have some duplication in reverseTranform method in the form and setPostTags in the model.

Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 5 years ago

Cheers!
i tried {{ render(controller('AppBundle\Controller\Admin\CategoryAdminController::newAction' )) }} but it says An exception has been thrown during the rendering of a template ("Class "AppBundleControllerAdminCategoryAdminController" does not exist.").
I tried {{ render(controller('AppBundle\\Controller\\Admin\\CategoryAdminController::newAction' )) }} but after some period of time simply blows out completely. Server simply crashes.
I tried {{ render(controller('AppBundle/Controller/Admin/CategoryAdminController::testAction')) }} but it says An exception has been thrown during the rendering of a template ("Class "AppBundle/Controller/Admin/CategoryAdminController" does not exist.").

Thank for your attention.

Reply

Ok, it's definitely the second option - with the \\. But why the server crashed? I'm not sure - that's very interesting. I would temporarily add an @Route("/test/tmp") annotation above the embedded controller and then go to /test/tmp to see if the page works on its own :).

Cheers!

Reply
Default user avatar
Default user avatar Abhishek Shrivastava | posted 5 years ago

Hi Team,

I want to ask how could I add Google map into my form. To create admin I am using EasyAdminBundle.

I have tried alot but I didnt get any solution for that please help.

Reply

Hey Abhishek Shrivastava!

Hmm, I'm not sure about this. But let me give you some high-level guidance:

1) Ultimately, in Symfony, you probably want to store some location data - perhaps latitude and longitude or maybe address? Let's assume latitude and longitude (it's a bit more complex). In your form, just make sure you have latitude and longitude fields.

2) Pretty much everything else will be done in JavaScript. We have a chapter about adding custom JavaScript to your page: https://knpuniversity.com/s.... What will your JavaScript need to do? Well, it will need to:

A) Initialize the google map and place it inside some element. You could even find your latitude and longitude fields and use jQuery to create a new element near them.
B) Add an event listener function so that your function is called when the user selects something on the map. I'm not really sure about this part, as I haven't used google maps in a long time.
C) Inside that event listener function, get the data you need in JavaScript (e.g. latitude and longitude) and then use jQuery to set the values on the two Symfony form fields (you could find them by their id's or give each some special js- class attribute.

That's it! It's all front-end work. Ultimately, when the user submits the form, they're just submitting normal fields filled with normal data. You could even hide the latitude and longitude fields if you don't want the user to even see this.

Good luck!

Reply
Default user avatar
Default user avatar Abhishek Shrivastava | weaverryan | posted 5 years ago

Hey Ryan,

Thanks for your quick response.

I just checked that and found it suits to my requirement but I am new to the Symfony so if you have any build in example related to Google map it will be more helpful to me.

Looking forward to hear from you soon.

-Abhishek

Reply

Hey Abhishek Shrivastava!

Unfortunately, I don't know of any example to show you! But, I will give you advice that I give many people: if Symfony's Form component is making things too hard for you, just don't use it! You can always create a normal HTML form (in this case, one that includes a google map) and then process the POST data manually in the controller (you can access the $_POST data via $request->request->get('foo') where foo is the name of the field. Or $request->request->all() for a full array of $_POST).

Good luck!

Reply
Default user avatar
Default user avatar Abhishek Shrivastava | weaverryan | posted 5 years ago | edited

weaverryan!

Thank you very much I will try to implement it and if I found any difficulty I will let you know.

-Abhishek

Reply
Guillem B. Avatar

Did you find the way? I'm really interested on this too!

Reply
Default user avatar
Default user avatar Gaétan Darquié | posted 5 years ago

It seems we have to add : "new JavierEguiluz\Bundle\EasyAdminBundle\EasyAdminBundle()," in AppKernell now.

Reply

Hey Gaetan,

You're right, and we use the same FQCN in this screencast, note the use statement in AppKernel.php ;)

Cheers!

Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 5 years ago | edited

I try to embed in list.html.twig template some form with javascript. I tried with this script


<script type="text/javascript">
    $(function(){
      html = '{{ include("admin/category/edit.html.twig") }}';
      $('table').on('click', '.js-category-edit', function(event){
        event.preventDefault();
        
        
      });
    })
  </script>

Idea is to modify after afterwards the action attribute and use a promise to modify category name but I got error in javascript. If I use the same include syntax in html tag I have no problem.

In Javascript I got incessantly:
Uncaught SyntaxError: Invalid or unexpected token


$(function(){
      html = '<h3>Edit Category</h3>\**I have a red cross right here**\

<form name="category_form" method="post">
  

  <div class="form-group"><label for="category_form_catTitle" class="required">Category</label><input type="text" id="category_form_catTitle" name="category_form[catTitle]" required="required" maxlength="255" class="form-control" placeholder="Category name" /></div>

  <div class="form-group">
    <input type="submit" class="btn btn-primary" name="submit" value="Save" />
  </div>

<input type="hidden" id="category_form__token" name="category_form[_token]" value="AlB6SZHwdEhQDe1vNV3_3f7bOq_bjKFqjownijJhDXY" />
    </form>
';

Is there something I can do?

Reply

Hi Diaconescu!

I know the issue! In JavaScript, you cannot add line breaks inside a string. I know, it seems kind of crazy :). But as soon as you add the new line after the h3 tag, that string is now a syntax error. Here's a similar, but better way to do what you want:


{#
    it's totally valid to put a little "template" inside a script tag. Your browser does not parse this,
    but it's available for you to read in JavaScript.
#}
<script type="text/template" id="admin-category-edit-template">
    {{ include("admin/category/edit.html.twig") }}
</script>

<script type="text/javascript">
    $(function(){
      var html = $('#admin-category-edit-template').html();
      $('table').on('click', '.js-category-edit', function(event){
        event.preventDefault();
      });
    })
</script>

Let me know if that helps!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial is built on an older version of Symfony & EasyAdminBundle. Many of the concepts are the same, but you can expect major differences in newer versions.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.3.*", // v3.3.18
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.7
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.17.0
        "sensio/distribution-bundle": "^5.0", // v5.0.25
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.4
        "knplabs/knp-markdown-bundle": "^1.4", // 1.7.1
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
        "stof/doctrine-extensions-bundle": "^1.2", // v1.3.0
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "javiereguiluz/easyadmin-bundle": "^1.16" // v1.17.21
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.7
        "symfony/phpunit-bridge": "^3.0", // v3.4.40
        "nelmio/alice": "^2.1", // v2.3.5
        "doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
    }
}
userVoice