JMS serializer. How to use Exclude condition? - php

i'm using JMS serializer in my symfony project and i have a question about "Exlude condition". Is it possible to specify that one property of entity would be exlude for all routes (methods) exept one.
I mean something like this:
/**
* #var string
*
* #ORM\Column(name="full_name", type="text", nullable=true)
* #JMS\Exlude(if="!someAction()")
*/
private $fullName;
If this possible, what is the correct syntax for this? Thanks)

To utilize #Exclude annotation, you need to have a bit of Symfony's ExpressionLanguage understanding.
Obviously, the function used in the annotation (e.g. someAction()) does not belong to the current object but rather to Expression language instance. To register it, do the following:
$language = new ExpressionLanguage();
$language->register('someAction', function(){}, function ($arguments, $object) {
// your logic goes here
return false;
});
Then bind it to your serializer:
$serializer = SerializerBuilder::create()
->setExpressionEvaluator(new ExpressionEvaluator($language))
->build();
Then you should be able to serialize using this exclusion strategy:
/**
* #var string
*
* #ORM\Column(name="full_name", type="text", nullable=true)
* #JMS\Exlude(if="!someAction(object)")
*/
private $fullName;
The one thing I am unsure of is passing empty callable to register call (for compiler) and I have no means of giving it a spin and confirming that is valid.
Hope this helps...

Related

Is doctrine Entity validation enough?

I have an Entity class which already has constrains:
/**
* #ORM\Column(type="string", length=255)
*/
private $X;
/**
* #ORM\Column(type="integer")
*/
private $Y;
In my controller i'm getting post data from form and using setMethods on entity class:
$property = new PropertyEntity();
$property->setX($request->request->get('X'));
$property->setY($request->request->get('Y'));
next step - save to db.
Do I need to do additional validation on post data ? I though I need to use validation library but i'm not sure if it will only add unnecessary overhead since "#ORM" is already form type is already doing some validation.
Any general idea how and where to write validation ?(pseudocode is enough)
Good question!
ORM mapping map the PHP class to the doctrine metadata (Model).
Assert is a mechanism to validate objects received from form (View/Controller).
This means that you can use assert on objects that are not entities or that you cannot use a mapped field in your formType
You can make validation in the annotation of the field. example:
/**
* #Assert\NotBlank
* #ORM\Column(type="string", length=255)
*/
private $X;
/**
* #Assert\NotBlank
* #ORM\Column(type="integer")
*/
private $Y;
Don't forget to add:
use Symfony\Component\Validator\Constraints as Assert;
More validation constraints are in this link:
https://symfony.com/doc/current/validation.html#basic-constraints

What is the return value of getSomethings in a Doctrine / Symfony One-To-Many Relationship?

I like to either type-hint or starting in PHP7 actually show the return value of a getter function. But with One-To-Many relationships in Doctrine / Symfony, I'm still stuck and am not sure what to add to the #var tag.
[...]
/**
* #var string
* #ORM\Column(name="name", type="string")
*/
private $features;
/**
* What goes into var here?
*
* One Product has Many Features.
* #ORM\OneToMany(targetEntity="Feature", mappedBy="product")
*/
private $features;
public function __construct()
{
$this->features = new ArrayCollection();
$this->name = 'New Product Name';
}
/**
* #return Collection
*/
public function getFeatures(): Collection
{
return $this->features;
}
[...]
Currently I’m using #var Collection and can then use the Collection functions. But what would be the »proper« thing to return? Is it indeed Collection? Or is it ArrayCollection? I’m tempted to use Features[] in order to use the functions of Feature, if I need to (instead of typehinting), but it doesn’t feel right.
What would be the »cleanest« / stable way to do this?
If you want to keep the docblock I would use the union type | to both specify the Collection and the list of values it contains like:
/**
* #var Collection|Feature[]
*/
With this your IDE should both find the methods from Collection as well as the Feature-type hints when you get a single object from the collection, e.g. in a foreach.
As to the question of ArrayCollection vs. Collection, it is usually recommended to type hint for the interface (Collection in this case). ArrayCollection offers a few more methods, but unless you really need them I would not bother with the type hint just to get them.
What I tend to do in projects is keep the Collection inside the entity and only pass out an array in the getter like this:
public function getFeatures(): array
{
return $this->features->toArray();
}
public function setFeatures(array $features): void
{
$this->features = new ArrayCollection($features);
}
Be careful, the void return type is not supported in PHP 7.0 yet. The benefit of returning an array is that in your code you don't have to worry about what kind of Collection Doctrine uses. That class is mainly used to maintain reference between objects inside Doctrine's Unit Of Work, so it should not really be part of your concern.

Best practise for placing custom Entity methods in Symfony

I'm creating Symfony project. And now I'm trying to find best practice for adding custom methods.. What is yours?
Visual explanation:
users table
id | name | surname
---+------+--------
1 | John | Smith
2 | Matt | Malone
Entity\User.php
namespace TestBundle\Entity\User;
use Doctrine\ORM\Mapping as ORM;
/**
* User
* #ORM\Table(name="users")
* #ORM\Entity
*/
class User
{
/**
* #ORM\Column(name="id", type="string", length=36)
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #ORM\Column(name="surname", type="string", length=255, nullable=true)
*/
private $surname;
/**
* OneToMany
*/
private $userCompanies;
{{ Setters and Getters }}
}
Where I should store custom method, like:
function getFullName()
{
return sprintf("%s %s", $this->getName(), $this->getSurname());
}
Or more complex:
function getCurrentUserCompany()
{
foreach ($this->getUserCompanies() as $company) {
if ($company->isActive()) {
return $company;
}
}
return null;
}
Please note, that all data returned via JSON
So far I tried extending class, but annotations not working as expected. Placing custom methods in same file looks trashy, since there will be more than one of them.
But.. but if there is repositoryClass - maby there is place for custom methods as well?
Thanks!
If it's about methods that are used mainly for display purposes then they are very similar to the getters, in my opinion they best fit is in the Entity itself, so inside your User.php class.
The repository is for defining methods for getting the entity from your storage level (DB, cache...), but the view level (your twig) should take the data from the entity itself.
If you need something more complicated or you need to reuse it, like a date filter, then it's better to create a Twig extension.
Methods like that belong to entity class and there is no reason to split code. If many entity classes share some methods, you can always create shared base abstract class or trait for them.
If you really want separated files for sake of your aesthetic, then use traits, but remember that it's not proper and conventional use of them.

Symfony2 Override Constraints

I have BaseEntity class:
class BaseEntity
{
/**
* The name.
*
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true, nullable=false)
* #Assert\Length(min=2, max=255, minMessage="default.name.short", maxMessage="default.name.long")
* #Assert\NotBlank(message = "default.name.not_blank")
*/
private $name;
}
and
class UserEntity extends BaseEntity
{
/**
* {#inheritDoc}
*
* #Assert\Length(min=2, max=255, minMessage="user.name.short", maxMessage="default.name.long")
* #Assert\NotBlank(message = "user.name.not_blank")
*/
private $name;
}
Now, When I post a new UserEntity into the form with long or short name Symfony gives me 2 errors: (for long:)
default.name.long
user.name.long
But I want it to show only ONE error, so :
- user.name.long
e.g. I want to override, but do not add another one
I think what you are looking for a validator group. So you can split up you validation rules into groups.
There is a excellent documentation about this feature:
http://symfony.com/doc/current/validation/groups.html
Maybe a Custom Validation Constraint could help you if you could (depending of your application logic) remove those two validations and make your own.
Something like this maybe?
http://symfony.com/doc/current/cookbook/validation/custom_constraint.html
If you're happy to set up at least some of your validation rules via a YAML file rather than annotations, you can override the base class's validation settings without needing to edit the class file itself.
Your YAML file would look something like this and would need to be in a location like src/YourApp/YourBundle/Resources/config/validation.yml to be picked up automatically:
BaseEntity:
properties:
name:
- NotBlank:
message: user.name.not_blank
- Length:
min: 2
minMessage: user.name.short
max: 255
maxMessage: default.name.long
If you want to put your validation file in a non-standard location, see https://stackoverflow.com/a/24210501/328817

Doctrine 2 annotations and "var"

Could someone tell me whats the meaning of "#var" in annotations configuration?
For example:
/**
* #Column(type="string", length=20, unique=TRUE)
* #var string
*/
protected $login;
It tells you what type of variable is it. Whether it's integer, string or an object, for example. It's used for auto-documentating processes
This is an annotation used for automatic documentation generation with phpDocumentor. For #var see their documentation.

Categories