unknow function error when calling twig extension - symfony

I'm looking to create a twig extension but Symfony keeps telling me my function is unknown.
Here is my class:
<?php
namespace AppBundle\Twig\Extension;
use Twig_Extension;
use Twig_SimpleFunction;
class FormExtension extends Twig_Extension
{
public function getFonctions()
{
$twigClass = 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode';
$options = array(
'node_class' => $twigClass,
'is_safe' => ['html']
);
return array(
'form_color' => new Twig_SimpleFunction($this, null, $options)
);
}
public function getName()
{
return 'app_form_extension';
}
}
And here is the service declaration in AppBundle\Resources\config\services.yml
services:
app.form_extension:
class: AppBundle\Twig\Extension\FormExtension
tags:
- { name: twig.extension }
I'm probably missing somehing but I can't find out what. Please help!

Correct getFonctions to getFunctions
Try this way:...
return array(
new Twig_SimpleFunction('form_color', null, $options)
);
...

For me same error was when I added my function to getFilters() instead of getFunctions()

Related

Guess custom form type when embedding custom classes

Two simple classes form my app model: Money and Product.
As Money app form being reusable, I've decided to create MoneyType extending AbstractType.
// App\Entity\Product
/**
* #ORM\Embedded(class="Money\Money")
*/
private $price;
// App\Form\ProductType
$builder->add('price', MoneyType::class)
// App\Form\Type\MoneyType
class MoneyType extends AbstractType
{
private $transformer;
public function __construct(MoneyToArrayTransformer $transformer)
{
$this->transformer = $transformer;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('amount', NumberType::class, [
'html5' => true,
'constraints' => [
new NotBlank(),
new PositiveOrZero(),
],
'attr' => [
'min' => '0',
'step' => '0.01',
],
])
->add('currency', ChoiceType::class, [
'choices' => $this->getCurrenciesChoices(),
'constraints' => [
new NotBlank(),
],
]);
$builder->addModelTransformer($this->transformer);
}
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'data_class' => null
]);
}
...
}
Is it possible to guess the field type without specifying it explicitly for obtaining the following code?
// App\Form\ProductType
$builder->add('price')
Any help is welcome. Thank you in advance.
You can implement a custom TypeGuesser that reads the doctrine metadata and checks if the field is an embeddable of the desired type. This is a basic implementation
namespace App\Form\TypeGuesser;
use App\Form\Type\MoneyType;
use Symfony\Component\Form\Guess\Guess;
use Symfony\Component\Form\Guess\TypeGuess;
use Symfony\Component\Form\FormTypeGuesserInterface;
use Doctrine\ORM\EntityManagerInterface;
class MoneyTypeGuesser implements FormTypeGuesserInterface
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function guessType($class, $property)
{
if (!$metadata = $this->em->getClassMetadata($class)) {
return null;
}
if (
isset($metadata->embeddedClasses[$property]) &&
'Money\Money' == $metadata->embeddedClasses[$property]['class']
) {
return new TypeGuess(MoneyType::class, [], Guess::HIGH_CONFIDENCE);
}
}
// Other interface functions ommited for brevity, you can return null
}
You can see all the interface methods that you need to implement here.
The Form TypeGuesser is mostly based on the annotation #var Money\Money and you should be able to build your down guesser for your own types, see https://symfony.com/doc/current/form/type_guesser.html
Also take a look at https://github.com/symfony/symfony/blob/4.3/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php on how to guess the type by doctrine orm specific types.
You could derive your own app specific guesser with those two examples.

JMS Serializer event is not working

I am sure it is a small error but I cannot find it.
I am trying to follow the official doc and implement an event listener on the pre_serialize event.
My service declaration:
<service id="app.question_serializer_subscriber" class="AppBundle\Serializer\QuestionSerializerSubscriber">
<tag name="jms_serializer.event_subscriber"/>
</service>
My subscriber:
<?php
namespace AppBundle\Serializer;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
class QuestionSerializerSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'method' => 'onPreSerialize',
)
);
}
public function onPreSerialize(ObjectEvent $event)
{
die('in event');
}
}
And my controller:
$question = $repo->findLastVersionByQuestionId((int) $questionId);
$serializer = SerializerBuilder::create()->build();
$context = new SerializationContext();
return new JsonResponse(json_decode(
$serializer->serialize(
$question,
'json',
$context
),
true
));
When I access the route my entity Question is serialized and displayed, but why does the die('in event'); is not displayed ?
Maybe it has a relation with the fact that my object is a Doctrine entity (issue 666 or PR 677 )
I finally find the issue. The problem is
$serializer = SerializerBuilder::create()->build();
This does not work but this does:
$serializer = $this->get('jms_serializer');
Try adding the class attribute, as example:
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'class' => 'FQCN_class_name',
'method' => 'onPreSerialize',
)
);
}
Another difference regarding the doc is in the argument of the class method: you should use PreSerializeEvent instead of ObjectEvent:
So like this:
public function onPreSerialize(PreSerializeEvent $event)
{
// ...
}
Check your service is correctly load from the container as example with the console command:
php app/console debug:container --tag=jms_serializer.event_subscriber
Hope this help

Using Subscribing Handler Interface when Serialize te modify parameters in symfony

I have a REST API and have an Entity Userwith field called Avatar, in DB I save name XXXX.jpg but when I return I want to add a url in this field Avatar, for example www.mylink.com/XXXX.jpg.
I'm trying with a service implements SubscribingHandlerInterfacebut I don't know how I can use it.
I have this method in this service:
class UrlManager implements SubscribingHandlerInterface
{
public static function getSubscribingMethods()
{
return array(
array(
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => 'AppBundle/Entity/User',
'method' => 'serializeUrlAvatar',
),
);
}
public function serializeUrlAvatar(User $user)
{
$url = 'www.mylink.com';
return array(
"avatar" => $url . $user->getAvatar()
);
}
}
but how can I call this service to modify url when I serialize.
Now I do this:
$_format = 'json';
$json = $this->get('jms_serializer')->serialize($user, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);
In service.yml:
app.url_converter_service:
class: AppBundle\Service\UrlManager
tags:
- { name: jms_serializer.subscribing_handler }
Update
In my controller I call this function like this:
$result = $this->get('app.url_converter_service')->serializeUrlAvatar($user);
$json = $this->get('jms_serializer')->serialize($result, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);
So my question is, exists a way to remove the first line and serialize correctly (add the url) when I serialize?
Have you registered your service like this?
# app/config/services.yml
avatar_url_handler:
class: YourBundle\Serializer\Handler\AvatarUrlHandler
tags:
- { name: jms_serializer.subscribing_handler }
I found a solution. I create a service which implements EventSubscriberInterface like this:
class UserSerializeHandler implements EventSubscriberInterface
{
private $user_uploads;
public function __construct($user_uploads){
$this->user_uploads = $user_uploads;
}
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'class' => User::class,
'method' => 'onPreSerializeUser'
));
}
public function onPreSerializeUser(PreSerializeEvent $event)
{
/** #var User $user */
$user = $event->getObject();
$avatar = $user->getAvatar();
$user->setAvatar($this->user_uploads . "/" . $avatar);
}
}
In service.yml:
app.serializer_user_service:
class: AppBundle\Service\UserSerializeHandler
arguments: ['%user_uploads%']
tags:
- { name: jms_serializer.event_subscriber }
I have user_uploads in parameters.yml like this:
user_uploads: 'https://myUrl.com'
And in any Controller that I serialize a User, I add the url in the Avatar paramter.
$json = $this->get('jms_serializer')->serialize($user, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);

Creating custom form type using sonata form type as parent

I'd like to make my own custom form type of sonata_type_collection, but I'm having trouble doing it.
At first I made my own type class and registered it as a service:
class SonataTypeCollectionWithSearchType extends AbstractType {
public function setDefaultOptions(OptionsResolverInterface $resolver) {
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add($options['type_options']['stcwst_attribute'], 'sonata_type_collection',array('data_class' => $options['type_options']['stcwst_class']));
}
public function getName()
{
return 'sonata_type_collection_with_search';
}
public function getParent()
{
return 'sonata_type_collection';
}
}
The service.yml:
services:
sonata_type_collection_with_search:
class: ASV\CoreBundle\Form\Type\SonataTypeCollectionWithSearchType
tags:
- { name: form.type, alias: sonata_type_collection_with_search }
In an UniversityAdmin class I added this to the formMapper:
->add('studycourses', 'sonata_type_collection_with_search', array(
'type_options' => array('delete' => false,
'stcwst_attribute' => 'studycourse',
'stcwst_class' => 'ASV\UniversityBundle\Entity\University'
), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position',
))
But now I'm getting this error:
FatalErrorException: Error: Call to a member function getAdmin() on a
non-object in
vendor/sonata-project/doctrine-phpcr-admin-bundle/Sonata/DoctrinePHPCRAdminBundle/Form/Extension/CollectionTypeExtension.php
line 32
Any ideas what went wrong? Is it even possible to have sonata_type_collection as a parent?

Symfony2 FOSUserBundle override profile form : field form empty?

I overrided registration form from FOSUserBundle with additionals fields: it works well.
When I apply the same logic to override Profile Form : the form appears well with my additionals fields but all is empty (the fields do not contain values ​​of the connected user).
Note: when I use the default form from the bundle the profile form contains the values ​​of the connected user.
Is there a specific action compared to override the registration form to retrieve the values ​​of the connected user ?
HERE IS CODE :
src/Vn/UserBundle/Resources/config/services.yml
services:
...
vn_user.profile.form.type:
class: Vn\UserBundle\Form\Type\ProfileFormType
arguments: [%fos_user.model.user.class%]
tags:
- { name: form.type, alias: vn_user_profile }
vn_user.form.handler.profile:
class: Vn\UserBundle\Form\Handler\ProfileFormHandler
arguments: ["#fos_user.profile.form", "#request", "#fos_user.user_manager"]
scope: request
public: false
symfony/app/config/config.yml
fos_user:
...
profile:
form:
type: vn_user_profile
handler: vn_user.form.handler.profile
src/Vn/UserBundle/Form/Type/ProfileFormType.php
namespace Vn\UserBundle\Form\Type;
use Symfony\Component\Form\FormBuilder;
use FOS\UserBundle\Form\Type\ProfileFormType as BaseType;
class ProfileFormType extends BaseType
{
public function buildUserForm(FormBuilder $builder, array $options)
{
parent::buildUserForm($builder, $options);
// custom field
$builder->add('profile',new MyProfileFormType(),array(
'label' => 'PROFILE'
));
}
public function getName()
{
return 'vn_user_profile';
}
}
src/Vn/UserBundle/Form/Type/MyProfileFormType.php
namespace Vn\UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class MyProfileFormType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('birthday','birthday', array(
'input' => 'array',
'widget' => 'choice',
'label' => 'Birthday',
))
->add('firstname','text', array(
'trim' => true,
'label' => 'Firstname',
))
->add('lastname','text', array(
'trim' => true,
'label' => 'Lastname',
))
->add('gender','choice', array(
'choices' => array('1' => 'Male', '2' => 'Female'),
'expanded' => true,
'required' => true,
'label' => 'Vous êtes',
));
}
public function getName()
{
return 'vn_user_myprofile';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Vn\UserBundle\Document\Profile',
);
}
}
I found the mistake in my file ProfilFormeHandler.php : in the function process() I called parent::onSucess() instead of parent::process() ...
The result is a "silent" bug (silent because not fatal error appears) due to my fault of course
Thanks for time you spent to try to help me, very sorry !
<?php
// src/Vn/UserBundle/Form/Handler/RegistrationFormHandler.php
namespace Vn\UserBundle\Form\Handler;
use FOS\UserBundle\Form\Handler\ProfileFormHandler as BaseHandler;
use FOS\UserBundle\Model\UserInterface;
class ProfileFormHandler extends BaseHandler
{
public function process(UserInterface $user)
{
//parent::onSuccess($user);
parent::process($user); // sound better of course : )
}
protected function onSuccess(UserInterface $user)
{
$this->userManager->updateUser($user);
}
}

Resources