Skip to content

Better merging of collection types#40

Closed
brutal-factories wants to merge 100 commits intoliip:1.xfrom
brutal-factories:improvement/collection-types
Closed

Better merging of collection types#40
brutal-factories wants to merge 100 commits intoliip:1.xfrom
brutal-factories:improvement/collection-types

Conversation

@brutal-factories
Copy link
Contributor

@brutal-factories brutal-factories commented Sep 15, 2023

When a model has a collection type-hinted with Collection, and the JMS serializer annotations has the actual information about the collections contents (@Serializer\Type("ArrayCollection<...>")), the model parsing fails with the error Can't merge type Liip\MetadataParser\Metadata\PropertyTypeClass with Liip\MetadataParser\Metadata\PropertyTypeArray, they must be the same or unknown.

This happens when the ReflectionParser passes before the JMSParser, due to the two property type metadatas PropertyTypeClass<Collection> and PropertyTypeArray<ArrayCollection<...>> not being compatible. Using JMS alone, this does work, and logically speaking, the PropertyTypeArray in the example can be understood to be a specialisation of the PropertyTypeClass.

This Pull Request proposes a specialized case to merge Collections from PropertyTypeClass into PropertyTypeArrays that are Collections too, using their most derived common base class (hopefully, both sides have the same collection type).

Here is a test script to explain my idea

<?php

require_once 'vendor/autoload.php';

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

use Doctrine\Common\Annotations\AnnotationReader;
use JMS\Serializer\Annotation as Serializer;
use Liip\MetadataParser\Builder;
use Liip\MetadataParser\Parser;
use Liip\MetadataParser\RecursionChecker;
use Liip\MetadataParser\ModelParser\JMSParser;
use Liip\MetadataParser\ModelParser\LiipMetadataAnnotationParser;
use Liip\MetadataParser\ModelParser\PhpDocParser;
use Liip\MetadataParser\ModelParser\ReflectionParser;
use Liip\MetadataParser\Reducer\TakeBestReducer;
use Liip\Serializer\Configuration\GeneratorConfiguration;

class Student {
    public ?string $name = null;

    /**
     * @Serializer\Type("DateTimeImmutable<'Y-m-d'>")
     */
    public ?DateTimeImmutable $createdAt = null;
    /**
     * @Serializer\Type("DateTime<'Y-m-d'>")
     */
    public ?DateTime $updatedAt = null;
}

class Course
{
    public ?string $name = null;
    /**
     * @Serializer\Type("ArrayCollection<Student>")
     */
    public ?Collection $students = null;

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

$parser = new Parser([
    new ReflectionParser(),
    new JMSParser(new AnnotationReader()),
    new PhpDocParser(),
    new LiipMetadataAnnotationParser(new AnnotationReader()),
]);

$builder = new Builder($parser, new RecursionChecker());
$metadata = $builder->build(IndirectAccess::class, [new TakeBestReducer()]);
$classes = [Student::class, Course::class];
$configuration = GeneratorConfiguration::createFomArray([
    'classes' => array_fill_keys($classes, []),
]);
@mkdir('./cached');
@mkdir('./cached/liip');
$cacheDirectory = 'cached/liip';
$deserializerGenerator = new \Liip\Serializer\DeserializerGenerator(new \Liip\Serializer\Template\Deserialization(), $classes, $cacheDirectory);
$deserializerGenerator->generate($builder);

$serializer = new \Liip\Serializer\Serializer($cacheDirectory);

var_dump($serializer->fromArray([
    'name' => 'Advanced php - II',
    'students' => [
        ['name' => 'Bob', 'created_at' => '2020-01-01', 'updated_at' => '2023-05-09'],
        ['name' => 'Alice', 'created_at' => '2021-02-12', 'updated_at' => '2023-05-09'],
    ]
], Course::class));

P.S.: I'll be adding proper auomated tests shortly Tests have been added to cover as many cases as I could think of.

dbu and others added 30 commits February 11, 2019 08:12
A code of conduct is good for every inclusive open source project. This one is standard.
cleanup naming and tweak version handling
Running checks and tests on CI now
use phpstan shim instead of having all phpstan code in vendors
This will hopefully make the step towards contributing a bit easier :)
Add phpunit, cs-fixer and travis config
fix test setup and support jms readonly annotation
Copy link
Member

@dbu dbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for this improvement. it makes a lot of sense to me.

i realize that the PropertyTypeArray is a bit of a misnomer, we should have called it PropertyTypeIterable to go with PHP iterable type... would that make sense to you?

as we do a BC break anyways, we could maybe also cleanup the name.

can you please add some tests for this? and a changelog entry?

i created the 2.x branch. can you please target that version.

@brutal-factories brutal-factories changed the title WIP: Better merging of collection types Better merging of collection types Sep 29, 2023
@brutal-factories brutal-factories requested a review from dbu October 4, 2023 10:47
Copy link
Member

@dbu dbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks a lot! i think this is about ready. i suggest to be forward compatible with our version 2, at least for the new PropertyTypeIterable.

please also add a note to the changelog. i think in the readme doc there is no explanation needed.

i created the 2.x milestone, please add to it.

@brutal-factories brutal-factories force-pushed the improvement/collection-types branch from b4971e0 to cc05f03 Compare October 4, 2023 17:15
@brutal-factories brutal-factories force-pushed the improvement/collection-types branch from cc05f03 to 4afb6a1 Compare October 5, 2023 10:35
@brutal-factories brutal-factories requested a review from dbu October 5, 2023 10:36
* - we're not merging a plain array PropertyTypeIterable into a hashmap one,
* - and the collection classes of each are either not present on both sides, or are the same, or parent-child of one another
*/
class PropertyTypeIterable extends AbstractPropertyType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should make this class final. if inverse what extends what we can do that, and have the deprecated class no longer be technically final.

@brutal-factories
Copy link
Contributor Author

It seems that force-pushing on my branch closed down my PR, I'll soon reopen it when my git directory is in order

@dbu
Copy link
Member

dbu commented Oct 18, 2023

it looks like what you did recreated every single commit since the very fist commit :-O

i think what i'd do is to create a fresh branch from 1.x and then cherry-pick the relevant commits.

git checkout 1.x
git pull
git checkout -b improvement/collection-types-take-2
git cherry-pick 57214bfe9f2d741ec5ae512df9fecfb50eb2d375
... and so on from your commits as per https://github.com/liip/metadata-parser/pull/40/commits (starting from old to new in order, otherwise you get conflicts)
git push origin HEAD -u

and create a new merge request.

@brutal-factories
Copy link
Contributor Author

This is reopened in #42

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.