Custom response of default operations of ApiPlatform - symfony

I'm trying to customize the default operations of API Platform.
I read the documentation of API Platform on the custom controller and operation, but I don't really understand, someone can explain me please ?
Context
I have two entities : User and Car related by a relation ManyToMany.
User.php
#[ApiResource()]
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
...
#[ORM\ManyToMany(targetEntity: Car::class, mappedBy: 'users')]
private Collection $cars;
...
}
Car.php
#[ApiResource()]
#[ORM\Entity(repositoryClass: CarRepository::class)]
class Car
{
...
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'cars')]
private Collection $users;
...
}
With ApiPlatform I can do all actions (Get, Post, Put...).
Question
When a user is login, he can use the API, when he try a GET on /api/cars, this return all cars.
How can I do for return only cars who are related to the user ?
Thanks to read me, have a good day

Check out API Platform documentation about CurrentUserExtension at : https://api-platform.com/docs/core/extensions/. Should help you.

Look at my answer on :
Symfony 6 Api Platform Extension
It almost the same usecase.

Related

API-Platform How to convert request data to Entity

I am fairly new to Symfony and API-Platform,
I have this problem where im trying to add a new entry in my entity
{
class ParentEntity {
private $id;
private $companyId;
private $company;
}
}
And for the security of Parent Entity's collection post, I wanna use the $company entity and process with its voter. somewhat like this
App\Entity\ParentEntity:
collectionOperations:
post:
security_post_denormalize: 'is_granted("HAS_ACCESS", request.company)'
Is there a way to access/convert the request data into the entity and access the Child Entity from there?
I have looked into #ParamConverter but I cannot understand how I can implement it with API-Platform (since there are no documentation related to this too)
Hope someone can help, thanks!

Symfony - Get Entity from within another entity

I would like to retrieve a record from another entity (or record from the DB) within a entity.
They there are no relationship between the two entities.
I am using #ORM\HasLifecycleCallbacks() and #ORM\PrePersist so when the main entity is created it will also create another entity (save a record to another table)
The above is working fine, there are no issues with this.
What I am having an issue with is I would like to link that entity with another table but I need to retrieve the object based on the value of the first entity.
Usually I would write a function in the entity repository but I am not calling the entity manager within the entity.
An Entity in Doctrine is an object representation of a concept, with attributes and methods. It is meant to be lightweight, a POPO (plain old php object). It must not know anything about its persistence. Therefore if you see reference to the EntityManager in a model, it probably stinks.
Solutions? You could use an entity listener called on entity creation and then use a service dedicated only to properly compose your object(s), maybe something like a Factory. In this way, your entity stays lightweight, the lifecycle management is satisfied and the entity composing is responsibility only of your service.
Entity manager is accessible in an entity repository. You can legally use it to fetch data from other entities and to compose your business logic. This is what entity repositories are made for: Doctrine Custom Repositories, Symfony Custom Repository Classes.
/**
* #ORM\Entity
*/
class Beta {}
/**
* #ORM\Entity
*/
class Alpha {}
class AlphaRepository extends EntityRepository
{
public function getDataFromAnotherEntity($something)
{
$query = 'select * from MyBundle\Entity\Alpha alpha where alpha.id = :something';
return $this->getEntityManager()
->createQuery($query)
->setParameter('something', $something)
->getResult();
}
}
In Symfony 3.1 you can use the entityManager to set a reference. This is still lightweight as it does not instance a complete Doctrine Record.
Example: I have an entity Status which has some states, and it's referenced in another entity. On create i use this method inside EventSubscriber:
public function preAction(LifecycleEventArgs $args)
{
$entity = $args->getObject();
$entityManager = $args->getObjectManager();
if (method_exists($entity, 'setStatus')) {
if ($entity->getStatus() === null) {
$entity->setStatus($entityManager->getReference('AppBundle\Entity\Status', Status::STATUS_REGULAR));
}
}
}

Doctrine event subscribers - performance and convention

I've got a basic question regarding the right way to do things in Symfony2, with specific emphasis on Doctrine event subscribers. I know how to implement them, but something has been bugging me. Currently, I have the following class.
namespace MyProject\MainBundle\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use MyBundle\MainBundle\Entity\LandingPageAggregator;
class LandingPageAggregatorSubscriber implements EventSubscriber {
/**
* Returns an array of events this subscriber wants to listen to.
*
* #return array
*/
public function getSubscribedEvents() {
return array(
'prePersist',
'preUpdate',
);
}
public function prePersist(LifecycleEventArgs $args) {
$entity = $args->getObject();
if (!$entity instanceof LandingPageAggregator)
return;
// Adittional stuff here...
}
}
I got this from this Symfony article, and it's working fine, my question is just the following:
Is there a better way to do this? Is this actually the accepted standard way of setting stuff like "Posted By" or "Created Date" fields?
Is this not performance intensive? Forgive me if I'm wrong, but registering 100 of these, doesn't that mean that for every single persist to the database, Symfony has to run thorugh all 100 subscribers and call the prePersist method on each of them? This seems like a giant waste of resources, thus the purpose of this question.
If I'm correct with number 2. above here, is there any better/less intensive method of doing the same thing? I just read on doctrine's documentation that they've introduced a new annotation as of 2.4, but I'm not using that version yet. Will that solve this issue in any case?
As a side question, what is the difference between the listener and subscriber as stated in the Symfony documentation linked above?
Thanks for any advice here!

FOSRestBundle / JMSSerializerBundle: interact with Symfony2 Security roles

I want to serialize and return only a few attributes of my entity with JMSSerializerBundle and FOSRestBundle.
For example I have this attributes:
User
Username
E-Mail
Birthday
Comments
Comments
Text
DateTime
Users with the role ROLE_ADMIN should get a serialized object of the whole user-object. ROLE_USER should only get the username and all comments.
What's the easiest way to implement the Symfony2 Security Component in JMSSerializerBundle? Or do I need to implement this in my controller and serialize it "by hand"?
Thank you very much
I don't think you have to do it all by hand. It sounds like serialization groups might be a good solution here.
use JMS\Serializer\Annotation\Groups;
/** #Groups({"admin", "user"}) */
$username
/** #Groups({"admin"}) */
$email
/** #Groups({"admin"}) */
$birthday
/** #Groups({"admin", "user"}) */
$comments
In your controller, it would just be a matter of checking the role and using the correct serialization group.
$serializer = $this->container->get('serializer');
$serializer->setGroups(array("admin")); or $serializer->setGroups(array("admin","user"));
Another option would be the JMSSecurityExtraBundle which lets you secure methods on your controller by role. Provide a different route/method for each option and let the bundle handle access control.

Extending entities in Symfony2 with Doctrine2

I'm having trouble finding a way to appropriately extend an Entity across bundles in Symfony2 using Doctrine2 as the ORM.
Currently there are three methods that I've found to extending entities in Symfony2 using Doctrine2 as the ORM. Mapped Superclass, Single Table Inheritance and Class Table Inheritance. None of these work for what I'm looking to do.
I have two Bundles: UserBundle and BlogBundle. I want to be able to use the UserBundle in projects that do not have the BlogBundle, but the BlogBundle will always be used in projects that have the User Bundle. It's ok if the BlogBundle has dependencies on the UserBundle, but not the other way around.
I have two entities:
BlogBundle\Entity\Post and
UserBundle\Entity\User
Relationship:
There needs to be a One to Many relationship between Users and Blog Posts. This is achieved through a Author_ID property (column) on the BlogBundle\Entity\Post object (table) which is mapped to UserBundle\Entity\User.id
The Problem:
I can call the UserBundle\Entity\User entity directly from within the BlogBundle and achieve what I'm looking for using a Uni-Directional mapping. This does not allow me to access all posts by a user from within a User object. I can access the data via custom queries but this is not as clean as accessing posts by a user through the user object.
What I'd like to do is extend the UserBundle\Entity\User object from within the BlogBundle, and add the methods and properties to this object that establish the One to Many mapping used within the BlogBundle. None of this is persisted, it simply defines the relationship and allows me to logically access all posts created by a user in an application that implements both the BlogBundle and UserBundle by adding needed functionality to the User object within the blog bundle (thus avoiding a dependency from the UserBundle to the BlogBundle).
When I create a BlogBundle\Entity\User object and extend UserBundle\Entity\User I must declare #ORM\Table(name="usertablename"). If I don't, any attempt to access the BlogBundle\Entity\User object will fail to access the database. Since none of the additions in the extended object persist, this works fine across bundles. The issue with this is when I call "php app/console doctrine:schema:update --force", there is a conflict since two entities try to map to & create the same table. I have tried using the ResolveTargetEntityListener feature that was recently implemented but this, along with Mapped Superclas, STI and CTI all force a dependency on the BlogBundle from the UserBundle.
Below are my objects to help illustrate my my setup. They have been abbreviated for clarity. I realize some of the semantics aren't correct but it's intended to communicate the ideas & configuration.
UserBundle\Entity\User
#ORM\Table(name="app_user")
#ORM\Entity
class User implements UserInterface
{
...
}
BlogBundle\Entity\Post
#ORM\Table(name="app_post")
#ORM\Entity
class Post
{
...
#ORM\Column(name="author_id", type="integer")
protected $author_id;
#ORM\ManyToOne(targetEntity="\App\BlogBundle\Entity\User", inversedBy="posts")
#ORM\JoinColumn(name="author_id", referencedColumnName="id")
protected $author;
}
BlogBundle\Entity\User
use App\UserBundle\Entity\User as BaseUser
#ORM\Entity
#ORM\table(name="app_user")
class User extends BaseUser
{
....
#ORM\OneToMany(targetEntity="App\BlogBundle\Entity\Post", mappedBy="author")
protected $posts;
public function __construct()
{
parent::_construct();
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
}
....
/* Getters & Setters, nothing that defines #ORM\Column, nothing persisted */
}
This works but the problem is that I'm mapping two entities in the project to the same table. The extended object doesn't grab the #ORM\Table(name="app_user") from it's parent so it must be defined in BlogBundle\Entity\User. If not any reference to this object from a controller will not access the database. Since nothing is persisted from the extended object nothing is broken except for when I try to update the database schema from the console.
I can use a unidirectional relationship, but this limits how I can access the data from within a controller.
You can see in this link to know about inheritance: http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html#single-table-inheritance
You must declare in UserBundle\Entity\User:
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"baseuser" = "UserBundle\Entity\User", "blogUser" = "BlogBundle\Entity\User"})
*/
class User implements UserInterface
{
...
}
And BlogBundle\Entity\User
use App\UserBundle\Entity\User as BaseUser;
/**
* #ORM\Entity
*/
class User extends BaseUser
{
....
}
Goodluck!
I think you could find this Bundle interesting:
https://github.com/mmoreram/SimpleDoctrineMapping
It allows you to define by parameters what files are mapping your entities, allowing to override every entity of your generic bundles.
For example:
parameters:
#
# Mapping information
#
test_bundle.entity.user.class: "TestBundle\Entity\User"
test_bundle.entity.user.mapping_file_path: "#TestBundle/Mapping/Class.orm.yml"
test_bundle.entity.user.entity_manager: default
test_bundle.entity.user.enable: true
The only contra I see is that you have to define all the next entities the same way cause you disabled auto_mapping...

Resources