Symfony api-platform: user should retrieve his own entities - symfony

I have this entity
/**
* #ApiResource()
* #ORM\Entity(repositoryClass="App\Repository\FeedRepository")
*/
class Feed implements AuthoredEntityInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="myFeeds")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="string", length=2083, unique=true)
*/
private $url;
// various getters and setters
}
using the AuthoredEntityInterface I made I can automatically set the user to the logged user.
I'd need to know how to set the collectionOperations so when I am logged in as the user with id = 1, when I call /api/feeds I will only retrieve items with user = 1. If this is possible I would like to do this with an annotation, otherwise any other method is ok.
thanks.

If it is just for connected user, what you need is a current user extension (doctrine extension). Else, you need to create a "subresource' link.
Link to Extension, and to Subresource.
Enjoy :) (and thank you timisorean for the review)

Related

SYMFONY, API PLATFORM how to add edit and show links to the serialized object

I'm working with SYMFONY and API PLATFORM to create REST API.
I have a Project Entity as an API Resource :
class Project
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $reference;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Gedmo\Slug(fields={"reference"})
*/
private $slug;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="create")
*/
private $createdAt;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="update")
*/
private $updatedAt;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity=Type::class, inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity=Status::class, inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $status;
With postman i get :
How can i add edit and show route to get a serialized object like this :
"hydra:member": [
{
...
"status": "/api/statuses/6",
"edit": "<a href='link_to_edit'>edit</a>", // add a link to edit
"show": "<a href='link_to_show'>show</a>" // add a link to show
},
knowing that i don't want to add edit and show to the entity properties or mapped them
Thanks for the help
Technically, you already have your edit and show routes (if you didn't customize them) : you only have to make a PUT or GET request to the value of the #id field of each object.
If you want to add an extra property to your entity, that isn't mapped you can do something like this :
/**
* #SerializedName("edit_route")
*
* #Groups({"projects:read"}))
*
* #return string
*/
public function getEditRoute()
{
return 'your_edit_route';
}
I wouldn't return HTML in this kind of field though, especially if your route is anything else than GET, and apps that use you API might not use HTML, so you're better off returning the simplest value and letting them do their thing with it.

Symfony/Doctrine: Accessing unmapped(?) data within entity

in Symfony (5.3.7 at present) I've got main data-entities and settings seperated. For example there is a user entity (default stuff), a TypeUserSetting defining the different settings and UserSetting which is m:n in between with the current setting stored.
namespace App\Entity;
/**
* #ORM\Entity(repositoryClass=TypeUserSettingRepository::class)
*/
class TypeUserSetting
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=200)
*/
private $description;
/**
* #ORM\OneToMany(targetEntity=UserSetting::class, mappedBy="setting")
*/
private $userSettings;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $default_value;
and
class UserSetting
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=4000, nullable=true)
*/
private $value;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="userSettings")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity=TypeUserSetting::class, inversedBy="userSettings",cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $setting;
Thats not that complicated so far...
My problem is, that oftenly settings don't exist, because the users did not set them. In that case I want to use the default.
class User ...
/**
* #ORM\OneToMany(targetEntity=UserSetting::class, mappedBy="user", orphanRemoval=true)
*/
private $userSettings;
...
public function getSettingById(int $id):string
{
foreach ($this->getUserSettings() as $oneSetting) {
if ($oneSetting->getId() === $id)
return $oneSetting->getValue();
}
//Not set, return the default
....
}
And here we go... If there is no setting, the loop fails and I want to get the default form the coresponding TypeUserSetting. This is not mapped, so I have to get it from the database, but I didn't find a way to access that properly.
Possible solutions I found that far:
Insert the UserSetting for all users by SQL when adding a TypeUserSetting. This would avoid the whole problem, but I simply don't like that.
Adding a static method to the TypeUserSetting-repo to get the value. I think that's ugly and somehow going back to last century...
Inject the TypeUserSetting-repo by LifeCycle-hooks which (in my oppinion) isn't the way entities should be used.
Injecting the repo from the controller that calls the function... I think this would be the opposite of encapsulation and separation of concerns. (and I think about hitting my head to the wall, just for having this kind of thoughts)
Anybody got a good idea to solve that?
Thanks in advance

Doctrine|ORM|Symfony: Is possible relation to Interface or multiple entities

Simple example:
I've got two users Admin and Client (both implements UserInterface) and Cart - three entity classes at a. Admin and Client can have his own carts. How to configure/resolve Cart entity relation to have method 'getUser()' which returns Admin or Client user?
Maybe I can have column user_id and second column with user entity name in Cart (something similar as DiscriminatorMapping can do)?
class Admin implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Cart", mappedBy="???")
*/
private $carts;
....
class Client implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Cart", mappedBy="???")
*/
private $carts;
....
class Cart
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var UserInterface
* #ORM\ManyToOne(targetEntity="UserInterface", ???)
*/
private $user;
....
I tried this Doctrine feature, also tried DisciminatorMapping and composite keys (join by multiple columns) option with no luck.
Any help?
I think you were almost there. DiscriminatorMapping is probably the way to go. However, you need to bind Client and Admin to a Parent class. So consider this hierarchy:
User (parent class)
Admin (extends User)
Client (extends User)
Then in your Cart entity you bind the relation to the User entity.

Symfony + JMSSerializer throw 500 - handleCircularReference

I'm trying to use the JMSSerializer with Symfony to build a simple json api.
So i have 2 simple Entities (1 User can have many Cars, each Car belongs to one User):
class Car
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="cars")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
}
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Car", mappedBy="user", orphanRemoval=true)
*/
private $cars;
}
Now i want to get all Cars with their User.
My Controller:
class CarController extends AbstractController
{
/**
* #param CarRepository $carRepository
*
* #Route("/", name="car_index", methods="GET")
*
* #return Response
*/
public function index(CarRepository $carRepository)
{
$cars = $carRepository->findAll();
$serializedEntity = $this->container->get('serializer')->serialize($cars, 'json');
return new Response($serializedEntity);
}
}
This will throw a 500 error:
A circular reference has been detected when serializing the object of
class \"App\Entity\Car\" (configured limit: 1)
Ok, sounds clear. JMS is trying to get each car with the user, and go to the cars and user ....
So my question is: How to prevent this behaviour? I just want all cars with their user, and after this, the iteration should be stopped.
You need to add max depth checks to prevent circular references.
This can be found in the documentation here
Basically you add the #MaxDepth(1) annotation or configure max_depth if you're using XML/YML configuration. Then serialize like this:
use JMS\Serializer\SerializationContext;
$serializer->serialize(
$data,
'json',
SerializationContext::create()->enableMaxDepthChecks()
);
Example Car class with MaxDepth annotation:
class Car
{
/**
* #\JMS\Serializer\Annotation\MaxDepth(1)
*
* [..]
*/
private $user;

Symfony2: Something like a Role Provider?

In my web application, I want my user's to be able to create roles and add users to them dynamically. The only thing I imagine, is to edit the security.yml every time, but this can't be the best solution, can it? It would be very nice, if there is something like a User Provider for roles, so I can define one which loads the roles from a database (doctrine).
Thanks for your help, hice3000.
Then, you should want to add a role Entity to your model Hice.
You have to know that Symfony2 provides support for dynamic roles too. You have a getRoles() method in the Symfony2 User spec in the API Doc, that your User entity should implement, that forces him to return Roles. These roles must either implement the role interface that specifies a getRole() method that returns, most usually, the role name itself.
You can then add the newly created role directly to your user role list that the getRoles() user method will then return.
Here is an example using annotations :
First role class
/**
* Role class
*
* #ORM\Entity()
*/
class Role implements RoleInterface, \Serializable
{
/**
* #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\ManyToMany(targetEntity="User", mappedBy="userRoles")
*/
private $users;
public function __construct()
{
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getRole()
{
return $this->name;
}
}
And the User class
/**
* User
*
* #ORM\Entity()
*/
class User implements UserInterface, \Serializable
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255)
*/
private $username;
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* #ORM\JoinTable(name="user_roles")
*/
private $userRoles;
public function __construct()
{
$this->userRoles = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getRoles()
{
return $this->userRoles->toArray();
}
I've skipped imports and methods to simplify the approach.
EDIT : There is something to know about serialization too. As Sharom commented on github, you musn't serialize users in roles neither roles in users. Just read his post and I think you'll understand :)

Resources