Code Monkey home page Code Monkey logo

zf-doctrine-hydration-module's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zf-doctrine-hydration-module's Issues

Reference hydration assumes document exists

When hydrating referenced documents the current logic assumes that an identifier will be passed and that a document with that identifier already exists. This may not be the case, however, and in the event that no identifier is passed it should be possible to instantiate a new document in lieu of an existing one.

entity_class setting should not be required

A hydrator should be able to hydrate any document passed. The entity_class setting implies having to specify a different hydrator for every entity in the system that one may wish to hydrate, which is impractical and cumbersome to implement for large systems.

Strategies from the service manager

Each of the Hydrator strategies are configured in the service manager as invokable services.
However within the module, the Hydrator strategy objects are instantiated from the class directly rather than via the service manager. For example in the EmbeddedReferenceField class:

        $strategy = new EmbeddedField($this->getObjectManager());
        $strategy->setClassMetadata($this->getClassMetadata());
        $strategy->setCollectionName($this->getCollectionName());
        $strategy->setObject($value);

It prevents being able to override the strategy via the service manager.

I know it is possible to configure custom strategies per field for an entity, however sometimes it may be desirable to set specific logic for all EmbeddedFields for example and therefore an option to set a custom EmbeddedFieldsStrategy without having to configure each specific embed field in every entity.

Multiple hydrator strategy per field

I'm, working with three entities: Albums, Artists and Tracks. They have a many-to-many relationship with doctrine. And I'm trying to update the Artists and Tracks list for a particular venue.
The way I'm trying to do so is by using PATCH.

//Adding Artists and Tracks to a specific Album entity
PATCH /Album/:album_id
{
  "artist": [
    {"id":123},
    {"id":456},
    {"id":789}
  ],
  "tracks": [
    {"id":111},
    {"id":222},
    {"id":333}
  ]
}

//Removing a particular Artist and two tracks from this Album.
PATCH /Album/:album_id?collection_action=remove
{
  "artist": [
    {"id":123}
  ]
  "tracks": [
    {"id":111},
    {"id":222}
  ]
}

//Removing a particular Artist and adding one track back to this Album.
PATCH /Album/:album_id?collection_action=remove&collection_remove=artist
{
  "artist": [
    {"id":123}
  ]
  "tracks": [
    {"id":222}
  ]
}

//Removing all artists and tracks from the Album.
//PATCH /Album/:album_id?collection_action=remove&collection_remove=artists,tracks
PATCH /Album/:album_id?collection_action=remove
{
  "artist": [],
  "tracks: []
}

A detailed example of what I'm proposing could be found zfcampus/zf-apigility-doctrine#215

Problem is, where in the modules could I specify this multiple strategies and make them dependent on the query being sent?

Embedded doc data not saved

Hi veewee, sorry to bother again.
I cannot get my head around the hydration of 'embedded docs' working with 'zf-apigility-doctrine'.

I have have a MappedSuperclass holding an EmbedDocument as per below code:
/**

  • @odm\MappedSuperclass

  • @odm\InheritanceType("COLLECTION_PER_CLASS")
    */
    abstract class Item {

    /** @odm\Id */
    protected $id;

    /** @odm\Field(type="string") */
    protected $brand;

    /** @odm\Field(type="string") */
    protected $proddesc;

    /** @odm\Field(type="int") */
    protected $quantity;

    /** @odm\Field(type="int") */
    protected $warranty;

    /** @odm\EmbedOne(targetDocument="PhysicalAttributes", strategy="set") */
    protected $physicalAttributes;

... getters and setters
}

The embedded doc is defined like this:

/**

  • @odm\EmbeddedDocument

  • */
    class PhysicalAttributes {

    /** @odm\Field(type="float") */
    private $width;

    /** @odm\Field(type="float") */
    private $depth;

    /** @odm\Field(type="float") */
    private $height;

    /** @odm\Field(type="float") */
    private $weight;
    ... getters and setters
    }

The concrete child is a very simple doc with 2 params as follow:
/**

  • @odm\Document(collection="singlesku")
    */
    class SingleSku extends Item {

    /** @odm\Field(type="boolean") */
    protected $isUnique;

    /** @odm\Field(type="string") */
    protected $googleGtin;

    ...getters and setters
    }

From POSTMAN I send the following POST data to create a 'singlesku' document:
{
"brand": "The Brand",
"proddesc": "Just a description!!!",
"quantity": 4,
"warranty": 5,
"isUnique": true,
"googleGtin": "GTin from Google",
"physicalAttributes": {
"width": 12.5,
"depth": 13.52,
"height": 28.654,
"weight": 543.97
}
}

The 'singlesku' document is successfully created in Mongo yet with no embedded 'PhysicalAttributes' data.

The JSON returned by Apigility is the following:
{
_isUnique: true
*googleGtin: "GTin from Google"
*id: "53c7887f05e17e65840041a8"
*brand: "The Brand"
*proddesc: "Just a description!!!"
*quantity: 4
*warranty: 5
-_physicalAttributes: {
width: 12.5
depth: 13.52
height: 28.654
weight: 543.97
}
-_links: {
-self: {
href: "http://localhost:8888/api/singlesku/53c7887f05e17e65840041a8"
}
}
}

Any help appreciated, as usual.

Embed Field Extraction Fails When Not Set

I have encountered a bug when an object is extracted that has at least one embed field that is either null or does not exist in the database (mongo).

The problem arises because the extract() method within the EmbeddedField Strategy class accepts a value of mixed type, however the Doctrine Hydrator extract() method that is subsequently called requires an object. Unfortunately the type is never tested and ends up in a get_class function call, as the value is NULL when not set in the DB, the get_class function call returns the name of the current class and exceptions are eventually thrown much further down the line.

EmbeddedField :

/**
     * @param mixed $value
     *
     * @return mixed
     */
    public function extract($value)
    {
        $hydrator = $this->getDoctrineHydrator();
        return $hydrator->extract($value);
    } 

https://github.com/phpro/zf-doctrine-hydration-module/blob/master/src/Hydrator/ODM/MongoDB/Strategy/EmbeddedField.php#L12

Note that the param annotation is mixed however the DoctrineHydrator class requires an object to its extract() method :

 /**
     * Extract values from an object
     *
     * @param  object $object
     * @return array
     */
    public function extract($object)
    {
        $this->prepare($object);

        if ($this->byValue) {
            return $this->extractByValue($object);
        }

        return $this->extractByReference($object);
    }

https://github.com/phpro/zf-doctrine-hydration-module/blob/master/src/Hydrator/DoctrineHydrator.php#L50

I have added a test case below, where the embed one is never set. This test fails because of an exception due to the non object value.

https://github.com/matwright/zf-doctrine-hydration-module/blob/hotfix/test-case-to-demonstrate-embed-field-break-when-not-set/test/src/Tests/Hydrator/ODM/MongoDB/Strategy/EmbeddedFieldTest.php#L26

I think the extract in the embed field strategy should either throw an exception or handle non objects by returning the same given value.

Naming Strategy + Filters Not Working

I'm using this module as a dependancy of ZF Apigility Doctrine module. I have configured a naming strategy service and filters as per instructions however my custom service is never invoked.

This is quite tricky to debug as I'm not certain whether the issue is within this module or ZF Apigility Doctrine or indeed elsewhere. However the features are part of this module so I though I would report it here to see if anyone has any feedback to help with my debugging.

What I have found so far is that my initial DoctrineObject is instantiated and the various filters and strategies attached, this object is of type Phpro\DoctrineHydrationModule\Hydrator\ODM\MongoDB\DoctrineObject and appears to be correctly configured with my custom filters etc.

However when the actual extraction takes place the object upon which the extract method is called is of type : DoctrineModule\Stdlib\Hydrator\DoctrineObject

A completely different object (different type and hash id ) which does not hold any reference to my custom filters/namingStrategies.

Therefore my extracted data is neither filters, nor the naming strategy processed.

I am continuing to work on this, but if you have any pointers that would be most grateful.

Default hydrator strategies

Thanks for this great module! Per hydrator strategies can be defined.
As feature request, it would be awesome if default strategies could also be defined that apply to all hydrators.

I use Apigility with Doctrine that makes use of this module. On all fields that are called "created" I want my UtcDateTimeStrategy to be set. So on every hydrator that apigility creates per rest service. I could add this manually in the configuration for every hydrator, but it is likely someone in the development team will forget one in the future.

Cannot install module by composer

This module uses old doctrine/doctrine-module": "^1.2" ...
Im using latest DoctrineORM an Doctrine modules..

doctrine/doctrine-module": "^1.2" versus doctrine/doctrine-module[2.1.4]

Fix unit tests; psr4

Unit tests fail because BaseTest.php is no longer in the Doctrine Mongo ODM library.

PSR-4 should be implemented on the source.

All tests should be re-namespaced to PhproTest\DoctrineHydrationModule.... so they are not in the same namespace as the library code.

Unable to use EntityManagerInterface implementation

The DoctrineHydratorFactory has a method called getObjectManagerType that throws an exception if the objectManager provided is not an instance of DocumentManager or EntityManager. If I have my own object manager that implements EntityManagerInterface this exception is hit.

I think this method should be updated to check against the interface, so that users may implement their own entity managers if desired

by_value option not respected

Regardless of the by_value setting, AbstractMongoStrategy::hydrateCollection always attaches AllowRemoveByValue strategy. When by_value is false it should attach AllowRemoveByReference instead.

Adds Laminas support

As Zend Framework is migrated now to Laminas and we have also released DoctrineModule 3.0 with Laminas support we need migrate also this library.

Hydrator configuration requires documentation

The current documentation has an example but it is unclear what the keys in the example configuration mean or how they should be used. Currently only the use_generated_hydrator configuration key has any documentation. All other keys should be documented, too.

php 5.4 requirement

Perhaps a requirement for php 5.4 could be added to composer.json.
(Since short array notation is used at least once, here).

Multiple properties with same strategy have field name set to last added strategy

Possibly a bug, need help.

It's as if all strategies get added by reference, as such, setting the property name on the strategy, sets it for all where this same instance has been added. They all have the same object hash.

Considering the following strategy configuration for a Resource:

    'doctrine-hydrator' => [
        'Company\\V1\\Rest\\Company\\CompanyHydrator' => [
            'entity_class' => \Namespace\Path\To\Company\Entity\Company::class,
            'object_manager' => 'doctrine.entitymanager.orm_default',
            'by_value' => true,
            'strategies' => [
                'users' => \ZF\Doctrine\Hydrator\Strategy\CollectionLink::class,
                'countries' => \Application\Strategy\CollectionUniDirectionalToManyStrategy::class,
                'currencies' => \Application\Strategy\CollectionUniDirectionalToManyStrategy::class,
            ],
        ],
    ],

To have your information complete, this is the entire custom strategy:

class CollectionUniDirectionalToManyStrategy extends AllowRemoveByValue
{
    public function extract($value)
    {
        return new Collection($value ?: []);
    }
}

So, when stepping through into the strategy during hydration for Countries, I expect to see that the collectionName of $this (instance of CollectionUniDirectionalToManyStrategy), is set to countries, however, it is set to currencies. See the debug image below for reference.

image

This is due to how these are set in the DoctrineHydratorFactory, here.

$strategy = $container->get($strategyKey);

Following that into the Zend ServiceManager we find this (here):

    // We start by checking if we have cached the requested service (this
    // is the fastest method).
    if (isset($this->services[$requestedName])) {
        return $this->services[$requestedName];
    }

This causes that the same instance is always returned. So when later the collection name is applied to 1 of the these "set" instances, it's applied to all of them. Creating the situation in the screenshot.


The bug fix:

Clone the result from the Zend ServiceManager to prevent using cached instances.

$strategy = clone $container->get($strategyKey);

5.3 compat

zf-apigility-doctrine, along with the rest of zfcampus, aims for php 5.3 compatibility. I'd like to edit this down but wanted to get a go-ahead from you @veewee first. Are you OK with moving this back to a 5.3 min?

New Feature: Provide for an Easy Way to Filter Fields

This issue is just meant to brainstorm about the possibility of providing an easy mechanism to filter fields once #8 is merged.

I was thinking we could provide an abstract factory that would simplify the process of filtering fields for entities and making that easy to version as well (based on API version). The idea is as follows:

// module configuration
'doctrine-hydrator' => [
    'Api\\V1\\Rest\\User\\UserHydrator' => [
        // .... other config
        'filters' => [
            'user-filter' => [
                'filter' => 'Api\\V1\\Rest\\User\\UserFilter'
            ],
        ],
    ],
],

The key Api\V1\Rest\User\UserFilter can obviously have a real service behind it. But by default it would hit a new ExtractFilterFactory abstract factory that will attempt to build any class that follows the regular expression: /^BEF.*?Filter$/ (we could change this). E.g. it would build a class named Api\V1\Rest\User\UserFilter.

To tie things up, we would provide a BaseExtractFilter class off which any filters can inherit. The class would implement the filter() method and automatically exclude any fields defined in a protected property of the instanced object.

At the end of the day, from a user's point of view, it would be possible to filter extraction output by simply following the config snippet above and defining the following class:

<?php
namespace Api\V1\Rest\User;
use Phpro\DoctrineHydrationModule\Utils\BaseExtractFilter;

class UserFilter extends BaseExtractFilter {
    protected $extractWhitelist = array(
        'id', 'firstname', 'lastname' // all other fields will not be extracted
    );
} 

If this is interesting then I have almost everything ready for a PR, just need to incorporate it into your namespaces etc (its currently implemented at the project level).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.