ZF2 Classmethods Hydrator working with RowGateway - php

i'm trying to implement the RowGateway class to my entities, I already have a form working with the entity and I'm trying to set the hydrator to work with ClassMethods.
I also noticed that ArraySerializable hydrator calls the populate() method or exchangeArray() and this method set the appropriate primary key when editing a row, unfortunately ClassMethods Hydrator doesn't do that.
What would be the best way to set the correct primary key value when using the Classmethod hydrator, should I set this value before binding the entity to the form? Or, should I extend the Classmethod H. to perform this task on initialize?

I'm not fond of using knowledge of the data layer in my entity. When using exchangeArray() you create mapping in the entity itself. I did some research about Zend's hydrators and came across serval posts including this one. Andrew's example of extending the ClassMethods hydrator seemed a good approach to map column names to getters/setters names.
When extending the ClassMethods hydrator you could also implement Zend\Stdlib\Hydrator\HydratorInterface.
For data manipulation use hydrator strategies.
http://framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.strategy.html
http://juriansluiman.nl/nl/article/125/strategies-for-hydrators-a-practical-use-case
To sepperate your entity over mutliple data sources you can use hydrator filters. For example, by default the ClassMethods hydrator extracts all entity methods starting with get.
http://framework.zend.com/manual/2.1/en/modules/zend.stdlib.hydrator.filter.html

You could extend Zend\Stdlib\Hydrator\ClassMethods and do any transformations you require here, assuming this is what you mean.
You can then use mapField to map from one of your fields to the correct id field name.
namespace Application\Model;
use Zend\Stdlib\Hydrator\ClassMethods;
class MyHydrator extends ClassMethods
{
/**
* Extract values from an object
*
* #param object $object
* #return array
* #throws Exception\InvalidArgumentException
*/
public function extract($object)
{
$data = parent::extract($object);
$data = $this->mapField('id', 'user_id', $data);
return $data;
}
/**
* Map fields
*
* #param type $keyFrom
* #param type $keyTo
* #param array $array
* #return array
*/
protected function mapField($keyFrom, $keyTo, array $array)
{
$array[$keyTo] = $array[$keyFrom];
unset($array[$keyFrom]);
return $array;
}
}
Alternatively you could make a getter and setter for the id field you need setting/getting, for example if you have an id called 'user_id' :
public function getUserId() { .. }
public function setUserId($id) { .. }

Related

Is it possible to annotate a generic container class in a docblock

Say I have a class Book which has a property on it called $chapters. In the implementation I'm working with, this property is represented by a generic helper class called Collection, which in turn contains an array of Chapter classes.
Example:
class Book {
/** #var Collection */
public $chapters;
}
class Collection {
private $items;
public function getItems(): array {
return $this->items;
}
}
class Chapter {}
Is it possible to annotate the $chapters property in Book so that my IDE knows that it is a Collection object, but that a call to that collection's getItems() method will return an array of Chapter instances?
Can I do this without creating a child class of Collection and annotating that?
EDIT: I don't think I was clear in my goal. I'm looking to type hint a class which is outside of the Book class and give guidance on what it's $items property would be — something like this (which I'm sure is invalid):
class Book {
/**
* #var Collection {
* #property Chapter[] $items
* }
*/
public $chapters;
}
For anyone who is still looking how to solve this issue, here is the solution based on Psalm's article "Templating".
Generics (templating)
If you you are working with variable data type inside a class (or even function), you can use what is called generic data types (in PHP this might be called templating). You can use generic data types for situations, when you need to write a piece of code, which doesn't really care of the type it uses, but it might be important for the client/user of the code. In other words: you want let the user of your code (Collection) specify the type inside your structure without modifying the structure directly. The best example (which is conveniently the anwswer itself) are collections.
Collections don't really need to know about what type of data they hold. But for user such as yourself this information is important. For this reason we can create generic data type inside your Collection and everytime someone wants to use the class they can specify through the generic type the type they want the Collection to hold.
/**
* #template T
*/
class Collection
{
/**
* #var T[]
*/
private $items;
/**
* #return T[]
*/
public function getItems(): array
{
return $this->items;
}
}
Here we defined our generic type called T, which can be specified by the user of Collection such as follows:
class Book
{
/**
* #var Collection<Chapter>
*/
public $chapters;
}
This way our IDEs (or static analyzers) will recognize the generic data type inside Collection as Chapter (i.e. the T type becomes Book).
Disclaimer
Although this is the way to write generic types in PHP (at least for now), in my experience many IDEs struggle to resolve the generic type. But luckily analyzers such as PHPStan knows how to work with generics.

PHP dynamic return type hinting

Suppose I have the following PHP function:
/**
* #param string $className
* #param array $parameters
* #return mixed
*/
function getFirstObject($className, $parameters) {
// This uses a Doctrine DQl builder, but it could easily replaced
// by something else. The point is, that this function can return
// instances of many different classes, that do not necessarily
// have common signatures.
$builder = createQueryBuilder()
->select('obj')
->from($className, 'obj');
addParamClausesToBuilder($builder, $parameters, 'obj');
$objects = $builder
->getQuery()
->getResult();
return empty($objects) ? null : array_pop($objects);
}
Basically, the function always returns either an instance of the class specified with the $className parameter or null, if something went wrong. The only catch is, that I do not know the full list of classes this function can return. (at compile time)
Is it possible to get type hinting for the return type of this kind of function?
In Java, I would simply use generics to imply the return type:
static <T> T getOneObject(Class<? extends T> clazz, ParameterStorage parameters) {
...
}
I am aware of the manual type hinting, like
/** #var Foo $foo */
$foo = getOneObject('Foo', $params);
but I would like to have a solution that does not require this boilerplate line.
To elaborate: I am trying to write a wrapper around Doctrine, so that I can easily get the model entities that I want, while encapsulating all the specific usage of the ORM system. I am using PhpStorm.
** edited function to reflect my intended usage. I originally wanted to keep it clean of any specific use case to not bloat the question. Also note, that the actual wrapper is more complex, since I also incorporate model-specific implicit object relations and joins ect.
I use phpdoc #method for this purpose. For example, I create AbstractRepository class which is extend by other Repository classes. Suppose we have AbstractRepository::process(array $results) method whose return type changes according to the class that extends it.
So in sub class:
/**
* #method Car[] process(array $results)
*/
class CarRepo extends AbstractRepository {
//implementation of process() is in the parent class
}
Update 1:
You could also use phpstan/phpstan library. Which is used for static code analyses and you can use it to define generic return types:
/**
* #template T
* #param class-string<T> $className
* #param int $id
* #return T|null
*/
function findEntity(string $className, int $id)
{
// ...
}
This can now be achieved with the IntellJ (IDEA/phpStorm/webStorm) plugin DynamicReturnTypePlugin:
https://github.com/pbyrne84/DynamicReturnTypePlugin
If you use PHPStorm or VSCode (with the extension PHP Intelephense by Ben Mewburn) there is an implementation named metadata where you could specify your own type-hinting based on your code doing the magic inside. So the following should work (as it did on VSCode 1.71.2)
<?php
namespace PHPSTORM_META {
override(\getFirstObject(0), map(['' => '$0']));
}

Can a Doctrine entity read itself?

just curious .. I have some code like so:
//$em is EntityManager of Doctrine
//$className is a type that uses polymorphism (subtype of a parent type)
$pricing = $em->getRepository($className)->findOneBy(array(
'active' => true,
'product_id' => (int) $this->id
));
//gets serialization of certain variables of $className
return $pricing->getSerialization();
But ... instead of calling findOneBy outside of $className, can I move getSerialization() method inside the Entity (which is $className), and return class-parameters from there?
I imagine it is not possible, since Entity cannot read itself. Correct?
The problem I am trying to solve is. ... In the example above Entity is populated via Doctrine and then it returns data. Therefore I have to use another class to populate the entity. Without Doctrine I know it's possible to do things such as read data from inside the Entity i.e. via mysqli, and then return properties directly or via a method. In other words, do I absolutely need another location (class/function/method outside of Entity) to populate the entity?
Sample Entity looks like so
class Pricing
{
function getSerialization(){}
/**
* #var integer #Column(name="id", type="integer", nullable=false)
* #Id
* #GeneratedValue(strategy="IDENTITY")
*/
protected $id;
//etc, a typical Doctrine Entity
}
Yes, the instance of an entity class can read itself.
But I guess your question should have been: "Can a Doctrine entity load and read itself?". The answer to that is no...
Loading of entities is managed by doctrine internals. If you would want the entity classes to load themselves it would mean injecting an EntityManager into the entity class.
This is a bad idea, I quote #BryanM. his answer on another stackoverflow question that covers this nicely:
It is not a good idea to allow an entity object to rely on the entity manager. It ties the entity to the persistence layer, which was a problem Doctrine 2 was specifically trying to solve. The biggest hassle in relying on the entity manager is that it makes your model hard to test in isolation, away from the database.
You should probably be relying on service objects to handle the operations that rely on the entity manager.
It means you need to take care of loading entities externally. I still don't see the problem with getSerialization. It can be inside the Entity class and can be used after the entity is loaded right?
If you want to do loading and serializing at once I would suggest making a PricingService in which you inject the repository or entity manager and where you define a public methods that does all that. For example:
<?php
use Application\Entity\Pricing;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
class PricingService
{
/**
* #var EntityManager
*/
protected $entityManager;
/**
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* #return EntityRepository;
*/
protected function getRepository()
{
return $this->entityManager->getRepository(`Application\Entity\Pricing`);
}
/**
* #param $params
* #return array
*/
public function findSerializedBy($params)
{
$pricing = $this->getRepository()->findOneBy($params);
return $pricing->getSerialization();
}
}
Now you can work with your PricingService directly:
$serializedPricing = $pricingService->findSerializedBy(array(
'active' => true,
'product_id' => (int) $this->id
));
You can of course generalize your service by adding another parameter with the $classname.

PHP Interfaces and argument inheritance

I have Entities and Repositories in my project. To simplify, I have
EntityInterface
UserEntity
BusinessEntity
Interface:
interface Entity
{
/**
* #return EntityId
*/
public function getId();
}
Implementations
class UserEntity implements Entity
{
/**
* #return EntityId
*/
public function getId(){
//...do something here for return
return $userId;
}
}
and
class BusinessEntity implements Entity
{
/**
* #return EntityId
*/
public function getId(){
//...do something here for return
return $userId;
}
}
I would like to define a Repository base-functionality, like save, so my interface looks like:
interface Repository
{
/**
* #param Entity $entity
*
* #throws \InvalidArgumentException If argument is not match for the repository.
* #throws UnableToSaveException If repository can't save the Entity.
*
* #return Entity The saved entity
*/
public function save(Entity $entity);
}
Later, I have different interfaces for different type of Repositories, like UserRepository and BusinessRepository
interface BusinessRepository extends Repository
{
/**
* #param BusinessEntity $entity
*
* #throws \InvalidArgumentException If argument is not match for the repository.
* #throws UnableToSaveException If repository can't save the Entity.
*
* #return Entity The saved entity
*/
public function save(BusinessEntity $entity);
}
The above code fails, because Declaration must be compatible with Repository...
however BusinessEntity implements Entity, so it's compatible.
I have many type of entities, so If I can't type-hint, I always need to check, that the passed instance is instanceof what I need. It's stupid.
The following code fails again:
class BusinessRepository implements Repository
{
public function save(BusinessEntity $entity)
{
//this will fail, however BusinessEntity is an Entity
}
}
In general, method parameters have to be contravariant with respect to an inheritance hierarchy or invariant. This means that indeed BusinessEntity would not be "compatible" with Entity when used as a type for a method parameter.
Think of it from a "contract" point of view. Your interface Repository promises that its method save can handle arguments of type Entity. Subtypes inheriting from Repository should be bound to this introduced contract (because otherwise, what sense would it make to define types in the first place, if you cannot be sure what they promises to be able to do?).
Now, if a subtype all of a sudden only accepts more special types, like BusinessEntity, but no longer Entity, the contract's broken. You cannot use BusinessRepository as Repository any more, because you cannot call save with an Entity.
This is counterintuitive at first, but have a look at this: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Contravariant_method_argument_type
Notice the inheritance arrow in the image.
What's to do? Get rid of the idea of inheritance being the holy grail in object oriented programming. Most of the time, it is not, and introduces all kinds of nasty coupling. Favor composition over inheritance, for example. Have a look at Parameter type covariance in specializations.
It fails because you declare methods that takes different arguments in interfaces. There is also question if there is any different logic in saving BusinessEntity than Entity. I think it shouldn't be. So you can omit save function in business entity and save just work on Entity and should know that Entity has "save" method.
The other way is to use factory pattern or abstract factory over inheritance.

Doctrine classes, can I add custom functions?

I'm having a difficult time finding if I can add custom functions to doctrine classes.
Lets say I have
use Doctrine\ORM\Mapping as ORM;
/**
* Map
*/
class Map
{
/**
* #var integer
*/
private $id;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
In my classes I would like some custom functions that return values that do not per se need to be stored in databse but merely provide a checking of certain values functionality.
For examply I would like to add a function isAboveTen();
function isAboveTen()
{
return this->id > 10;
}
Can I just go ahead and do this or do I need to define them as a special field in the xml file or annotations?
You can safely add functions working on simple member types, Doctrine will ignore them if you do not add any annotations.
The question whether you should avoid doing this depends on your overall architecture and coding guidelines. As mentioned in the comments both flavors with logic possibly inside vs outside the entities exist.
However, you should keep in mind that:
All persistent properties/field of any entity class should always be private or protected, otherwise lazy-loading might not work as expected. In case you serialize entities (for example Session) properties should be protected (See Serialize section below).
Which is described in the documentation. Since you are accessing these members inside your class, magic methods like __get() will not be called.

Categories