Api platform, prevent POST method when some datas are the same - symfony

I'm using symfony with API PLATFORM to create a contacts share system :
One User can share one or many contacts to one or many other users.
I created ContactShare entity :
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=ContactShareRepository::class)
*/
class ContactShare
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity=Contact::class, mappedBy="contactsToShare")
*/
private $contacts;
/**
* #ORM\ManyToOne(targetEntity=AppUser::class, inversedBy="shareContactsOwner")
*/
private $sender;
/**
* #ORM\OneToMany(targetEntity=AppUser::class, mappedBy="shareContactsReceivers")
*/
private $receivers;
...
...
}
When i tested with postman i can post a new share :
My question is :
How to add constraints or validators to prevent the POST method of contact share if we
share the same contacts to the same user, and Prevent POST method if we send the same
contact to a duplicated user ?

You can create a custom validator and call valid as shown here.
If validation returns one (or more) error, you should return some kind of response with proper HTTP status code (4xx?).
Moreover, if this validation should be performed only for this API call (I think this is a domain requirement, so it should be always applyed, but it's only a guess of mine) you can take advantage of validation groups.
UPDATE: API Platform seems to take care of this on your behalf, so you don't need to call explicitly valid

Related

Symfony Assert Min Value

I have a video entity that only contains a video from YouTube. To do this, I have to add a constraint in the entity with an assert, I tried "Range" with minimum: https://youtube.com/watch?v= and by specifying the string group. It’s no working! Help me! Here is the code of the entity:
/**
* #ORM\Column(type="string", length=255, nullable=true)
* #Assert\Range(
* min = "https://youtube.com/watch?v=",
* groups = {"string"}"
* )
*/
private $video;
You should try to solve this task with a custom validation constraint. It allows your code to assert that a request parameter contains https://youtube.com/watch?v= or even to check that video url is a valid video (with request to a Youtube API with a help of Guzzle client or Youtube API library). More info see in the official Symfony docs. https://symfony.com/doc/current/validation/custom_constraint.html

Doctrine 2.5. OneToOne relationship sharing primary key

In my app, when users sign up I have to send them an email with a validation key, as usually happens on most websites, I'm trying to do this with Doctrine but I can’t get it to work when I try to persist() the user.
First of all, I think the correct way in this case is to use a OneToOne unidirectional relationship, but I don’t know if it would be better to use a bidirectional one. I've tried both and I always get an error.
I have read these two questions carefully:
One to one relationship on two tables sharing primary key
Doctrine one-to-one unidirectional
As well as this part of the documentation. When I validate the schema (php bin/console doctrine:schema:validate) everything is fine.
class Usuario {
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
// ...
}
class ClaveVal {
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="Usuario")
*/
private $usuario;
/**
* #ORM\Column(name="clave", type="string", length=20, nullable=false)
* #Assert\NotBlank()
*/
private $clave;
// ...
}
Which is quite similar to this.
Now, I'm trying to persist() a new Usuario and a new ClaveVal for this usuario like this:
$usuario = new Usuario();
// Add usuario attributes
$claveVal = new ClaveVal();
$claveVal->setUsuario($usuario);
$claveVal->setClave(‘123456’);
$em->persist($usuario);
$em->persist($claveVal);
But I get this error:
The given entity of type 'AppBundle\Entity\ClaveVal'
(AppBundle\Entity\ClaveVal#000000007020f28f0000000031d5c8c6) has no
identity/no id values set. It cannot be added to the identity map
I know why this happens. This works perfectly:
$em->persist($usuario);
$em->flush();
$em->persist($claveVal);
$em->flush();
But I don't want to do that because I want it to be a unit of work using flush() only once.
Besides, as the author of the post I linked above says, it should be Doctrine's job to flush() at the right moment to get the id.
So, how can I achieve this using flush() only once (and without using transactions or listeners, I'm sure there is an easier way to do this)? Would it be better to use a bidirectional OneToOne relationship? As I said, I tried it too but I got the same error.
Thanks in advance.

Custom metadata information in Symfony/Doctrine

Is it possible to create custom metadata information in the entities? Something that would use the already present functionality of using either Annotation, yml or xml to store metadata about the entity.
Example:
/**
* #var string
*
* #ORM\Column(name="text", type="text")
* #CUSTOM\Meta(key="value") // <-- Extra information
*/
protected $text;
For what I've been researching, it seems that I should use the functionality of ClassMetadataFactory. Is it possible, or would I have to make it from scratch?

Access control lists in Symfony2 using FOSUserBundle - adding roles to a user

I'm hoping this is more simple than the docs I've been reading.
I have a number of entities with ManyToOne relationships with a standard FOSUserbundle user entity that can login users, register them, logout etc.
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
}
Each of my users can also have a number of pets. I.e. here's the cat entity (simplified):
class Cat
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
**/
private $user_id;
How should I add an access control list user role that defines if the user has any cats or not, and if they do, allows them onto the "cats only" part of my website.
Note (slightly related): I feel mildly retarded when it comes to database design using doctrine, I'll upvote anyone that can point me to a good tutorial / explanation of designing things with different kind of joins, especially if it's a in Symfony/Doctrine environment.
Well, since you haven't gotten anything else yet. I'll throw something out there but my experience is only moderate so far.
In the controller for your cats owners page, you could query if the user has any associated cat entities.
You need to add OneToMany/ManyToOne annotations to your user and cat class, and a variable for the associations. This you could review straight out of the doctrine section of the symfony2 book (If you are working with Symfony2 and haven't taken the time to read the book, that is the minimal level of knowledge to work with the full stack and I suggest you read it all). (Do you really need a entity class just for cat, you could have a pet entity with a "type" option.)
check the security context to ensure logged in, then query db.
$em = $this->getDoctrine()->getManager();
$ownersCats = $em->getRepository('PetsPetSiteBundle:Blog')->findBy(array('type'=>'cat'));
or for one result only
$ownersCats = $em->getRepository('PetsPetSiteBundle:Blog')->findOneBy(array('type'=>'cat'));
Once you have this result if it comes back false, you know he has no cats. Otherwise continue.
Obviously you could make this perform better by only querying for id's or something if you don't plan on using the entities on that page or just performing a count query and seeing if it's greater than 0?

Email user not required

I would like set email to not required on my User entities. How do what ?
I've searched in FOSUserBundle/config/ but i don't find any "required" parameter about email.
Following Tib's answer, here is the way I did it. It's working fine :
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity
* #ORM\AttributeOverrides({
* #ORM\AttributeOverride(name="email", column=#ORM\Column(type="string", name="email", length=255, unique=false, nullable=true)),
* #ORM\AttributeOverride(name="emailCanonical", column=#ORM\Column(type="string", name="email_canonical", length=255, unique=false, nullable=true))
* })
*/
class User extends BaseUser {
// ...
}
EDIT :
The previous solution is easy to implement and was working fine, but after updating Symfony to version 2.1.8 and without changing anything in my app, generating entities failes throwing a Doctrine mapping exception (Invalid field override ...)
In spite of loosing a lot of time trying to solve that issue, I was just stucked.
Then I finally realized that FOSUserBundle provides lot of features I don't use, but doesn't really fit my needs, which are quite basic.
I need to disable email requirement because my app user management is "software like" : no registration, no email sending, no profil, user and roles managed only by admin, etc...
I was adapting a tool not designed for my needs. So I decided to make my own implementation of Symfony security.
It is a little more work, but it's not very difficult and the result is far better and more flexible than adapting FOSUserBundle. Of course, for a "classic" website or web app, FOSUserBundle is the best.
If you are in this case too, I suggest you to do so. Here is the doc to begin : Security in the Book and Security in the CookBook
I hope this can help you.
Sorry for my poor english, I hope it is intelligible
Check this PR https://github.com/doctrine/orm-documentation/pull/117/files and look at #AttributeOverrides
You need to override following methods in the entity User.
class User extends BaseUser // inheritance from fos
{
/**
* #var string
* #ORM\column(type="string", name="email", length=255, unique=true, )
* #Assert\Email(
* checkMX = true
* )*/
protected $email
}
}
You need to change Table definition to:
class User extends BaseUser // inheritance from fosUserBundle
{
/**
* #var string
* #ORM\column(type="string", name="email", length=255, unique=false, nullable=true )
*/
protected $email
}
}
Same thing is needed for emailCanonical attribute.
If that not pass. You will need to copy entire model from FOS into your bundle and then change it as I wrote.
check the doc: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/1.2.0/Resources/doc/doctrine.md
Regards Max

Resources