вторник, 1 февраля 2022 г.

[Doctrine] how to replace Many-To-Many to three entities

When you need to put new field/database columns into Many-To-Many, relationship, you will need to switch to intermediate entity. In my case it is StudentKlass.

Imagine three entities: Team - StudentTeam - Student

use Doctrine\ORM\Mapping as ORM;

class Team {
/**
* @ORM\OneToMany(targetEntity=StudentTeam::class, mappedBy="team", cascade={"persist"})
*/
private $studentTeams;

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

class StudentTeam {
/**
* @ORM\ManyToOne(targetEntity=Student::class, inversedBy="teams")
* @ORM\JoinColumn(name="student_id", referencedColumnName="id")
*/
private $student;

/**
* @ORM\ManyToOne(targetEntity=Team::class, inversedBy="studentTeams")
*/
private $team;
...
}

class Student {
/**
* @ORM\OneToMany(targetEntity=StudentTeam::class, mappedBy="student")
*/
private $teams;
private $name;
private $email;
...

} 

Also don't forget to create getters, adders & removers!

You can use example how to use it in Symfony forms, which has example how to use nested forms:

class TeamType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('studentTeams', CollectionType::class, [
'entry_type' => TeamStudentType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
]);
...
}

class TeamStudentType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add(
$builder
->create('student', FormType::class, [
'data_class' => Student::class,
])
->add('email', TextType::class)
->add('name', TextType::class)
)
;
}
}
}

пятница, 28 января 2022 г.

[Symfony] "FormType too few arguments passed 0, 1 argument needed" fix

It can be not only FormType, also can be any your custom form type. 

It can be when you moved your form into new namespace/bundle, but did not registered services in it. 

  1. don't forget that your form should extend Symfony\Component\Form\AbstractType;
  2. add new bundle namespace in composer.json
  3. Create bundle extension file in ./DependencyInjection/FooExtenstion like this https://symfony.com/doc/current/bundles/extension.html
  4. add this into your bundle services.yaml:
services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true

    FooBundle\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'
            - '../src/Tests/'
        5. run php bin/console cache:clear

четверг, 27 января 2022 г.

[hateoas] [jms] how to set custom serialization group

 The issue is using Hateoas + JmsSerializerBundle with custom serialization groups don't respond with embedded with needed fields, for me they respond only with {}. The workaround is make \JMS\Serializer\Exclusion\DisjunctExclusionStrategy::shouldSkipProperty() to return with false.

You can make it with custom class, which implements ExclusionStrategyInterface before all standard Strategies in Context like this:

$view->getContext()->addExclusionStrategy(new CustomDisjunctStrategy([]));

That's it, after it it should work (but without links). If you use Sylius bundles, you can use it in overriden ViewHandler like this:

<?php

namespace App\Serializer;


use FOS\RestBundle\View\View;

use FOS\RestBundle\View\ViewHandler as RestViewHandler;

use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;

use Sylius\Bundle\ResourceBundle\Controller\ViewHandlerInterface;

use Symfony\Component\HttpFoundation\Response;


final class ViewHandler implements ViewHandlerInterface

{

    /** @var RestViewHandler */

    private $restViewHandler;


    public function __construct(RestViewHandler $restViewHandler)

    {

        $this->restViewHandler = $restViewHandler;

    }


    /**

     * {@inheritdoc}

     */

    public function handle(RequestConfiguration $requestConfiguration, View $view): Response

    {

        if (!$requestConfiguration->isHtmlRequest()) {

            if ($requestConfiguration->getSerializationGroups()) {

                $groups = $requestConfiguration->getSerializationGroups();

                $view->getContext()->addExclusionStrategy(new CustomDisjunctStrategy([]));

            } else {

                $groups = [];

            }

            $this->restViewHandler->setExclusionStrategyGroups($groups);


            if ($version = $requestConfiguration->getSerializationVersion()) {

                $this->restViewHandler->setExclusionStrategyVersion($version);

            }


            $view->getContext()->enableMaxDepth();

        }


        return $this->restViewHandler->handle($view);

    }

}



# services.yaml:

sylius.resource_controller.view_handler:

        class: App\Serializer\ViewHandler

        arguments:

            - "@fos_rest.view_handler"

понедельник, 10 января 2022 г.

понедельник, 6 декабря 2021 г.

how to fix "keypair size should be SODIUM_CRYPTO_BOX_KEYPAIRBYTES bytes"

 Check that you copied private key, instead of public key. Because their length differs from each other

понедельник, 29 ноября 2021 г.

[JMS serializer] how to set naming strategy

     public function serialize($object, array $serializationGroups)

    {

        $serializer = SerializerBuilder::create();

        $serializer->setPropertyNamingStrategy(new SerializedNameAnnotationStrategy(new IdenticalPropertyNamingStrategy()));

        $serializer->addMetadataDir($this->configDir . "/serializer", 'App\Entity');

        $serializer->setCacheDir($this->cacheDir . "/jms_serializer");

        $serializer = $serializer->build();


        $group = new GroupsExclusionStrategy($serializationGroups);

        $context = SerializationContext::create();

        $context->addExclusionStrategy($group);


        return $serializer->serialize($object, 'json', $context);

    }

It will make JSON string where property field is camelCased instead of Snake_cased. I highly recommend to use 'App\Entity' in addMetadataDir() because by default in Symfony jms_serializer.yaml config is set 'namespace_prefix' in metadata node, and without 'App\Entity' argument YML/XML entity configs won't work. It is also example how to customize Serializer parameters manually with SerializerBuilder without framework config.

вторник, 9 ноября 2021 г.

[Symfony] Cannot read index 'email' from object of type App\Entity\User because it doesn't implement \ArrayAccess

 Its because PropertyAccessor thinks form is an array. To change it, set form data_class. For example in your form Type:

public function configureOptions(OptionsResolver $resolver)

    {

        $resolver

            ->setDefaults([

                'data_class' => User::class,

...