Coder Social home page Coder Social logo

zend-hydrator's Introduction

zend-hydrator's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zend-hydrator's Issues

is and has are not extracted like get in ClassMethods hydrator

Hello,

when extracting value from an object with the ClassMethods hydrator, the extracted array returns array keys which are not compatible as the array keys which are used for hydrating.

Is this so expected and i'm just expecting another behavior falsely?

Example:

// my object

class MyClass 
{
    protected $active = true;

    public function isActive() : bool
    {
        return $this->active;
    }

    public function setActive(bool $active)
    {
        $this->active = $active;
    }
}

$hydrator = new \Zend\Hydrator\ClassMethods();

var_dump($hydrator->extract(new MyClass())); // returns ['is_active' => true]

// returns class Myclass#12 { protected $active => bool(true) }, expected  { protected $active => bool(false) }
var_dump($hydrator->hydrate(['is_active' => false], new MyClass())); 

// returns class Myclass#12 { protected $active => bool(false) }  as expected
var_dump($hydrator->hydrate(['active' => false], new MyClass()));

Shouldn't extraction remove the is like it does for get on extraction (see line 159ff in ClassMethods.php)?

thanks in advance

CamelCaseToUnderscoreFilter

Hey there,

I've created #101 with a failing unit test.
The CamelCaseToUnderscoreFilter filters the name totally different than the old one:

zend-filter + strtolower (as in zend-hydrator v2)

bfd7b82e9cfceaa82704d1c1_foo

CamelCaseToUnderscoreFilter (as in zend-hydrator v3)

bfd_7_b82e_9_cfceaa_82704_d1c_1_foo

MapNamingStrategy vs. ArrayMapNamingStrategy

Do we need both? From what I can deduce (from source and the PR that introduced ArrayMapNamingStrategy) they're nearly identical except:

  • MapNamingStrategy's argument is a hydration map and ArrayMapNamingStrategy is an extraction map. Both then flip the array to get the mapping for the opposite direction.
  • MapNamingStrategy allows overriding the reverse map, in case you want an asymmetrical mapping.

updating zend-hydrator to 2.0 via composer oddity

Hi
I have come across a strangeness I can't explain and think is a bug. When updating zend-hydrator via composer to version 2 it reverts other Zend modules to earlier incompatible versions the output is:

Updating dependencies (including require-dev)
- Removing zendframework/zend-stdlib (2.7.4)
- Installing zendframework/zend-stdlib (2.6.0)
Loading from cache

- Removing zendframework/zend-hydrator (1.0.0)
- Installing zendframework/zend-hydrator (2.0.0)
Loading from cache

- Removing zendframework/zend-modulemanager (2.6.1)
- Installing zendframework/zend-modulemanager (2.5.3)
Loading from cache

- Removing zendframework/zend-form (2.6.0)
- Installing zendframework/zend-form (2.5.3)
Loading from cache

- Removing zendframework/zend-mvc (2.6.0)
- Installing zendframework/zend-mvc (2.5.3)
Loading from cache

- Removing zendframework/zend-db (2.6.2)
- Installing zendframework/zend-db (2.5.2)
Loading from cache

And when I change the zend-hyrator back to '1.*' it all goes back to the lastest '2.*' Zend modules
I am sure this can't be right, has anyone got any insights into this or am I the only one experiencing this?

Thanks inadvance

Doubled first row when HydratingResultSet use buffer() on Oracle database

  • I was not able to find an open or closed issue matching what I'm seeing.
  • This is not a question. (Questions should be asked on chat (Signup here) or our forums.)

Hi, i use a OCI8 (oracle) adapter for an application in Expressive. zend-db and zend-hydrator is installed. I need sometime to use buffer() function to iterate more than one time in the result.

It appear that the first row is doubled in the result when i use buffer(). When i'm not use buffer the result is correct.

I also check with MariaDB database for testing and it works in both case (with and without buffer).

I don't know if that problem are really in zend-hydrator or somewhere else because it happened in my case with oracle only.

  • PHP 7.1.x / 7.3.x
  • zendframework/zend-hydrator (3.0.2)
  • zendframework/zend-db (2.10.0)
  • OCI8 Version (2.2.0)

Thanks!

Code to reproduce the issue

Just a partial code to see

        /*
        * Test with MariaDB
        */

        $sql = sprintf("MariaDB SQL query that retreive 5 records");

        $result = $this->mariadbAdapter->createStatement($sql)->execute();

        // Hydrating, not totally configured for that test
        $hydrator = new ObjectPropertyHydrator();

        $resultSet = new HydratingResultSet($hydrator, new TestEntity());
        $resultSet->initialize($result);
        $resultSet->buffer(); // With or without the use of buffer, all works like expected



        /*
        * Test with Oracle
        */

        $sql = sprintf("Oracle SQL query that retreive 5 records");

        $result2 = $this->oracleAdapter->createStatement($sql)->execute();

        // Hydrating, not totally configured for that test
        $hydrator2 = new ObjectPropertyHydrator();

        $resultSet2 = new HydratingResultSet($hydrator2, new TestEntity());
        $resultSet2->initialize($result2);
        $resultSet2->buffer(); // -> First row is doubled if buffer enabled!!


        /* In view $resultSet is on $this->mariadbDatas and $resultSet2 on $this->oracleDatas */

	<h2>MariaDB Results:</h2>
	<!-- MariaDB results -->
	<?php foreach($this->mariadbDatas as $row): ?>
	    <pre>
	    <?php print_r($row) ?>
	    </pre>
	<?php endforeach; ?>

	<h2>Oracle Results:</h2>
	<!-- Oracle results -->
	<?php foreach($this->oracleDatas as $row): ?>
	   <pre>
	   <?php print_r($row) ?>
	   </pre>
	<?php endforeach; ?>

Expected results

A
B
C
D
E

Actual results with buffer() // oracle only

A
A <- first line doubled in the results
B
C
D
E

ClassMethods throws exception on missing setter when hydrate object

Hello,

I'm not sure if its bug or feature. In method ClassMethods::hydrate is used php function is_callable to determine if setter is callable or not. But this function only checks syntax and does not check if setter really exists or is public. In this case function is_callable always returns true.

So instead of is_callable there should be method_exists, but there is a catch. Function method_exists only checks if method really exists, but doesn't care if is public.

I think there should be this condition:

(new \ReflectionClass($object))->hasMethod($setterName) && (new \ReflectionMethod($object, $setterName))->isPublic()

In tests should be this:

        $classMethodsCamelCase = $this->hydrator->hydrate(
            ['fooBarBaz' => 'baz-tab'],
            new ClassMethodsCamelCase()
        );
        $classMethodsCamelCaseMissing = $this->hydrator->hydrate(
            ['fooBarBaz' => 'baz-tab'],
            new ClassMethodsCamelCaseMissing()
        );

        $this->assertSame('baz-tab', $classMethodsCamelCase->getFooBarBaz());
        $this->assertSame('2', $classMethodsCamelCaseMissing->getFooBarBaz());

Strategy name in conjunction with NamingStrategy

Problem

Zend-Hydrate does not work as expected when I use strategies and NamingStrategy together. The problem is, that the strategies have to be bound to the property name when you hydrate an object with data. But when you extract data from an object, the strategies have to be bound to the array key. The only exception is ClassMethods-Hydrator which works slightly different. This is a flaw in my opinion as every hydrator should handle strategies and naming strategies identically (see #45).

Possible solutions

  1. Strategies must be bound to the property name. AbstractHydrator::hydrateValue und AbstractHydrator::extractValue get called with the property name (NamingStrategyInterface::hydrate) provided.
  2. Strategies must be bound to the array key. AbstractHydrator::hydrateValue und AbstractHydrator::extractValue get called with the array key (NamingStrategyInterface::extract) provided.
  3. Strategies can either be bound to the property name or array key. AbstractHydrator::hydrateValue und AbstractHydrator::extractValue get called twice: At first with the property name (NamingStrategyInterface::hydrate) provided. Then again with the array key (NamingStrategyInterface::extract) provided.

I would go with the first approach because objects have the stricter structure and I usually add strategies to cast an array value to a specific type or create an object out of the value. The array structure may change faster than the class.

Feel free to comment and don't hesitate to correct me, if I have missed an important point.

Examples

Pseudo examples with Zend\Hydrator\NamingStrategy\UnderScoreNamingStrategy. This is how hydrators work (except ClassMethods, see above):

Hydration

$arrayKey = 'foo_bar';
$propertyName = $hydrator->getNamingStrategy()->hydrate($arrayKey); //fooBar
$hydrator->hydrateValue($propertyName, $data[$arrayKey], $data); //Hydrate value with strategy named equal to $propertyName => 'fooBar'

Extraction

$propertyName = 'fooBar';
$arrayKey = $hydrator->getNamingStrategy()->extract($propertyName); //foo_bar
$hydrator->extractValue($arrayKey, $object->$propertyName, $object); //Extract value with strategy named equal to $arrayKey => 'foo_bar'

Flaw

$hydrator->addNamingStrategy(new UnderscoreNamingStrategy());
$hydrator->addStrategy('fooBar', new PseudoStrategy());
$extracted = $hydrator->extract($hydrator->hydrate($object, $data));
/*
$extracted['foo_bar'] and $data['foo_bar'] will not be identical because PseudoStrategy::extract was never called.
You have to bind the same strategy to both, "fooBar" and "foo_bar", to get the expected result.
$hydrator->addStrategy('fooBar', $strategy);
$hydrator->addStrategy('foo_bar', $strategy);
*/

Updates to help zend-stdlib transition

Since we have no tags yet, I'm going to propose the following course of action in order to facilitate a smooth transition from zend-stdlib's hydrators to this repository.

  • Remove the final keyword from any classes, and tag v1.
  • Re-instate the final keyword in classes that had them, and tag v2.

This will allow zend-stdlib to pin to v1 during the transition series (2.7+), and have its own classes extend any zend-hydrator classes in that series, marking them as @deprecated.

v3

This is a proposal I've made long time ago, and move it as a separate repository until this one would be created: https://github.com/zf-fr/hydrator

Some things should likely be changed, but from my memories the new hydrator was more flexible, much more efficient.

If someone would like to take it and submit a PR here. Maybe the awesome @weierophinney ?

zend-hydrator requires package zend-filter

Zend-Hydrator package uses Zend\Filter\FilterChain as a type hint and thus has a dependency on zend-filter. However, it only recommends it as a package. I recommend updating composer to add zend-filter as a required package.

Error:

PHP Fatal error:  Class 'Zend\Filter\FilterChain' not found in /Users/jeremygiberson/gitprojects/coolsurfin/vendor/zendframework/zend-hydrator/src/NamingStrategy/UnderscoreNamingStrategy.php on line 57

allow string name as HydrationInterface::hydrate() second parameter

The docblock above the definition of hydrate in the HydrationInterface says that the second parameter should be an object.

This implies that the second argument must be an object already costructed, that we are going to mutate to make it hydrated.

I would like to use the hydrator to construct a new object so that I can pass the array values directly to the constructor of my object, in such a way that the object is created already in a valid state and there is no need of mutation to hydate it completely.

This would be possible just allowing the second parameter to be a class name, and not a class instance.

I understand that this would make the hydrator also a factory, but in my opinion the benefits exceed this drawback.

What do you think?

Mili/microseconds problem with DateTimeFormatterStrategy

I use postgres with datetime with timezone columns. Possible value types are:

$dates = [
    '2016-03-04 10:29:40+01',
    '2016-03-04 10:29:40.123456+01',
];

I think there is no way to create parse format with optional part - in this example miliseconds.

Imo this can be solved with little DateTimeFormatterStrategy hydrate modification:

    public function hydrate($value)
    {
        if ($value === '' || $value === null) {
            return;
        }

        if ($this->timezone) {
            $hydrated = DateTime::createFromFormat($this->format, $value, $this->timezone);
        } else {
            $hydrated = DateTime::createFromFormat($this->format, $value);
        }

        if (false === $hydrated) {
            // this is fallback when createFromFormat does not work
            $hydrated = new DateTime($value, $this->timezone);
        }

        return $hydrated ?: $value;
    }

Thoughts?

possible BC break

after ZF2.5.3 release many components got updated and hydrator also:

...
  - Removing zendframework/zend-stdlib (2.5.2)
  - Installing zendframework/zend-stdlib (2.7.4)
    Downloading: 100%         

  - Installing zendframework/zend-hydrator (1.0.0)
    Downloading: 100%         
...

after update throws this exception

PHP Catchable fatal error: Argument 1 passed to FarmActivity\Hydrator\Factory\ActivityHydratorFactory::FarmActivity\Hydrator\Factory{closure}() must be an instance of Zend\Stdlib\Hydrator\Aggregate\ExtractEvent, instance of Zend\Hydrator\Aggregate\ExtractEvent given in /var/www/farmmanager/module/FarmActivity/src/FarmActivity/Hydrator/Factory/ActivityHydratorFactory.php on line 34

I see that Zend\Stdlib\Hydrator\Aggregate\AggregateHydrator is deprecated so I assume someone missed something with backwards compatibility :-)

Unexpected behaviour when hydrating objects from Form inputs and from Database

If we use strategies attached to hydrator to do hydration, a method is called AbstractHydrator::hydrateValue($name, $value, $data = null). If we attach a strategy to hydrator using addStrategy() method, for hydration to take place, a $name must 100% match a key in the $data array. Here is the problem. Usually in the forms, people give names to <input>s with first letter lower-case, like someName. In databases, the column names are usually with the first letter capital, like SomeName. Then the name of the strategy you attach via addStrategy() starts to depend on where you get the $data array from: if from database, you must name strategy as SomeName (first letter capital), if from form, you must name the strategy someName (first letter lower-case) for object hydration to succeed. The two together cannot live.

This's quite annoying. Perhaps someone could fix it.

No issue, just a silly question...

..to be closed at once...

I've always wondered about the HydrationInterface method signature;

public function hydrate(array $data, object $object)

Why doeasn't the direct object come first?
public function hydrate(object $object, array $data)

hydrate($theTargetOfTheAction, $withThisData)

The naming strategy hydrator use the target / direct object aka the $name as first argument

sorry for asking, kind regards

This part break apigility admin

public function filter(string $property) : bool

when create a new field on REST service or try to open REST service php throw exception.
the reason is argument for filter(string $value) is integer.

'input_filter_specs' => [
    "POST" => [
        0 => [ // Argument 1 passed to Zend\Hydrator\Filter\FilterComposite::filter() must be of the type string, integer given
            'required' => true,
            'validators' => [],
            'filters' => [],  
            'name' => 'test_filed', 
        ],
    ],
],

@weierophinney can you please review this changes,
it`s done by this commit 7b6d85e#diff-164cac30a1f18281587e41a02a3ee036

Cannot edit/remove items from collections

Since 9457a25, it’s not possible to remove an item from a collection. See also my original comment to that commit.

Example

Consider a blog post that has a headline (string) and a set of tags (string[], since it’s just an example). In initial state, the headline is not empty and the post has a single tag:

$original = [
    'headline' => 'A new post',
    'tags'     => ['zend-hydrator']
];

I’d like to remove the “zend-hydrator” tag. Assume I did so using something-like-Zend\Form. The following should be the final state:

$replacement = [
    'headline' => 'An updated post',
    'tags'     => []
];

To make this change happen, the ArraySerializable hydrator merges the initial state with the final state and returns the result. Internally, it used to use (before 9457a25) array_merge, that made the result look like this:

array(2) {
  ["title"]=>
  string(15) "An updated post"
  ["tags"]=>
  array(0) {
  }
}

Since the mentioned commit, array_merge was dropped in favor of the Zend\Stdlib\ArrayUtils::merge() method, which does the merging a bit differently:

array(2) {
  ["title"]=>
  string(15) "An updated post"
  ["tags"]=>
  array(1) {
    [0]=>
    string(13) "zend-hydrator"
  }
}

In case of collections of items, I believe this behavior is incorrect, since I’m unable to remove anything from such collection.

AggregateHydrator's EventManager overwritten due to EventManagerAwareInterface

I have a Service Factory that creates an AggregateHydrator. I add my hydrators in the factory which in turn attaches events to the event manager. However, because the AggregateHydrator implements the EventManagerAwareInterface, the EventManager that contains all the events is replaced with the global EventManager and therefore renders my factory useless.

Is there a recommended way of doing this?

FilterComposite issue with Swoole 4.4.0 and higher

  • I was not able to find an open or closed issue matching what I'm seeing.
  • This is not a question. (Questions should be asked on chat (Signup here) or our forums.)

Provide a narrative description of what you are trying to accomplish.

From Swoole v4.0.0, the extension replaces 'array_walk' and 'array_walk_recursive' with its own versions.

An excerpt fro the release notes under new features:

When RuntimeHook is turned on, the function array_walk, 
array_walk_recursive will be replaced by the version of Swoole, 
which will solve the problem that the native function cannot be reentrant, 
but it will not be able to traverse object

The replacement triggers the below error from within \Zend\Hydrator\Filter\FilterComposite constructor:

Argument 2 passed to swoole_array_walk() must be callable, array given

Wrapping the 'array_walk' function in 'Closure::fromCallable' solves the problem (PHP 7.3, Swoole 4.4.1), but it may not be a comprehensive solution for the library's support matrix.

Extending from ClassMethods not possible

Unfortunately, after updating from zend-hydrator to version 3, our existing classes are no longer functional.

In the methods hydrate () and extract () the strict type "object" throws an error.

Apparently, it is not thought that these classes will be extended. How should we implement this and change our application?

class UserDbHydrator extends ClassMethodsHydrator implements HydratorInterface
{
    protected $colPrefix = 'user_';
    protected $colIgnoreList = ['username', 'password'];

    public function hydrate(array $data, object $object)
    {
        $array = $this->removePrefix($data, $this->colPrefix);
        $hydrated_array = parent::hydrate($array, $object);
        return $hydrated_array;
    }

    public function extract(object $object): array
    {
        $extracted_array = parent::extract($object);
        $array = $this->appendPrefix($extracted_array, $this->colPrefix, $this->colIgnoreList);
        return $array;
    }

    public function appendPrefix($array, $prefix, $ignore)
    {
        $data = [];
        foreach ($array as $key => $value) {
            if ((!preg_match('/^' . $prefix . '/', $key)) and (!in_array($key, $ignore))) {
                $key = $prefix . $key;
            }
            $data[$key] = $value;
        }
        return $data;
    }

    public function removePrefix($array, $prefix)
    {
        $data = [];
        foreach ($array as $key => $value) {
            if (preg_match('/^' . $prefix . '/', $key)) {
                $key = preg_replace('/^' . $prefix . '/', '', $key);
            }
            $data[$key] = $value;
        }
        return $data;
    }
}

Aggregate hydrator EventManager via factory overwritten by Mvc\ServiceManagerConfig initializer

We have a specific strategy for mappers to hydrate entities using the AggregateHydrator and it's events. We have a hydrator factory which initializes an AggregateHydrator and adds a ClassMethods hydrator. In ZF3, because the AggregateHydrator implements the EventManagerAwareInterface and Mvc\ServiceManagerConfig explicitly adds an EventManagerAwareInitializer, AggregateHydrator's EventManager is explicitly overwritten post-factory and ClassMethods is lost.

To summarize:

ServiceManager
{
    doCreate
    {
        FactoryInterface
        {
            new AggregateHydrator
            AggregateHydrator->add(ClassMethods) // Creates new EventManager
        }()

        EventManagerAwareInitializer {
            AggregateHydrator->setEventManager($container->get('EventManager')) // No ClassMethods
        }
    }
}

undeclared dependency on zend-servicemanager

This package has a dependency on zend-servicemanager but this dependency is not declared in the composer.json.

An example of this would be the Zend\ServiceManager\FactoryInterface in the HydratorPluginManagerFactory.php file.

I would suggest updating the composer.json file to include zend-servicemanager as a dependency.

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.