Get collection without deleted resources API-Platform - symfony

How can I select a resource without it's releted resources deleted using API-Platform ?
I have this relation Don ManyTOOne Donateur. In the Don resouce, I have $isDeleted as field. When its value is true, and a try to select a item form Donateur, I get it.
Show here:
// The GET operation http://localhost:8000/api/donateurs/3
{
"#context": "\/api\/contexts\/Donateur",
"#id": "\/api\/donateurs\/3",
"#type": "Donateur",
"id": 3,
"nom": "Yazid Ibn Amr",
"solde": 135000,
"isDeleted": false,
"dons": [
{
"#id": "\/api\/dons\/1",
"#type": "Don",
"id": 1,
"date": "2021-07-26T00:00:00+00:00",
"montant": 35000,
"isDeleted": true
},
{
"#id": "\/api\/dons\/2",
"#type": "Don",
"id": 2,
"date": "2021-07-28T00:00:00+00:00",
"montant": 60000,
"isDeleted": false
},
{
"#id": "\/api\/dons\/3",
"#type": "Don",
"id": 3,
"date": "2021-07-28T00:00:00+00:00",
"montant": 75000,
"isDeleted": false
}
]
}
I don't need deleted resource in this returned collection of Don when getting a Donateur item.
Here is my code
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\DonRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Don"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get"={
* "normalization_context"={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* }
* },
* "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonRepository::class)
*/
class Don
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read:Donateur:item"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Donateur::class, inversedBy="dons")
* #ORM\JoinColumn(nullable=false)
* #Groups({"read:Don:collection", "write:Don"})
*/
private $donateur;
/**
* #ORM\Column(type="date", nullable=true)
* #Groups({"read:Don:collection", "write:Don", "read:Donateur:item"})
*/
private $date;
/**
* #ORM\Column(type="float")
* #Groups({"read:Don:collection", "write:Don", "read:Donateur:item"})
*/
private $montant;
And
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\DonateurRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Donateur:item",
* "read:Donateur:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Donateur"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get", "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonateurRepository::class)
*/
class Donateur
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups("read:Donateur:collection")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"read:Donateur:collection", "write:Donateur"})
*/
private $nom;
/**
* #ORM\Column(type="text", nullable=true)
* #Groups({"read:Donateur:collection", "write:Donateur"})
*/
private $presentation;
/**
* #ORM\Column(type="float")
* #Groups({"read:Donateur:collection", "write:Don"})
*/
private $solde;
/**
* #ORM\Column(type="boolean")
* #Groups("read:Donateur:collection")
*/
private $isDeleted;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity=User::class)
*/
private $createdBy;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $updatedAt;
/**
* #ORM\ManyToOne(targetEntity=User::class)
*/
private $updatedBy;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $deletedAt;
/**
* #ORM\ManyToOne(targetEntity=User::class)
*/
private $deletedBy;
/**
* #ORM\OneToMany(targetEntity=Don::class, mappedBy="donateur")
* #Groups({"read:Donateur:item"})
*/
private $dons;
And this one
<?php
namespace App\Doctrine;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use Doctrine\ORM\QueryBuilder;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use App\Entity\Don;
use App\Entity\Donateur;
use App\Entity\TypeOeuvre;
class NoneDeletedResources implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
public function addWhere(QueryBuilder $queryBuilder, string $resourceClass)
{
if($resourceClass === TypeOeuvre::class or $resourceClass === Don::class or $resourceClass === Donateur::class){
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
$queryBuilder->orderBy("$rootAlias.id", "DESC");
$queryBuilder->setParameter("isDeleted", false);
}
}
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?string $operationName = null)
{
$this->addWhere($queryBuilder, $resourceClass);
}
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?string $operationName = null, array $context = [])
{
// dd($context);
$this->addWhere($queryBuilder, $resourceClass);
}
}
Thank you for helping

$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
With this code, you're only filtering on the root alias, not on its relations. You should alter your code in such a way that it filters on those relations as well.
public function addWhere(QueryBuilder $queryBuilder, string $resourceClass)
{
if($resourceClass === Donateur::class) {
$rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
$queryBuilder->orderBy("$rootAlias.id", "DESC");
$queryBuilder->setParameter("isDeleted", false);
// add something like this:
$queryBuilder->andWhere("dons.isDeleted = :isDeleted");
}
}
The exact name of the alias (I used dons as an example) depends on your use case. Maybe you should join Don before you can use it.
I hope you can use my suggestion to fix your problem. If not, please let me know; I'll help with adding some implementation details.

public function addWhere(QueryBuilder $queryBuilder, string $resourceClass)
{
if($resourceClass === TypeOeuvre::class or $resourceClass === Don::class or $resourceClass === Donateur::class or $resourceClass === Oeuvre::class or $resourceClass === DonOeuvre::class){
$rootAlias = $queryBuilder->getRootAliases()[0];
if($resourceClass === Donateur::class) {
$queryBuilder->join("$rootAlias.dons", "d");
$queryBuilder->andWhere("d.isDeleted = :isDeleted");
}
if($resourceClass === Oeuvre::class or $resourceClass === Don::class) {
$queryBuilder->join("$rootAlias.donOeuvres", "d");
$queryBuilder->andWhere("d.isDeleted = :isDeleted");
// dd($queryBuilder);
}
$queryBuilder->andWhere("$rootAlias.isDeleted = :isDeleted");
$queryBuilder->orderBy("$rootAlias.id", "DESC");
$queryBuilder->setParameter("isDeleted", false);
}
}
The first if is working correctly. But not the second
This is my resources code:
Don.php
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\DonRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Don"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get"={
* "normalization_context"={
* "groups"={
* "read:Don:item",
* "read:Don:collection"
* }
* }
* },
* "utilisation_don" = {
* "path" = "/{id}/utilisation-don,
* "controller" =
* }
* "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonRepository::class)
*/
class Don
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read:Donateur:item"})
*/
private $id;
...
/**
* #ORM\OneToMany(targetEntity=DonOeuvre::class, mappedBy="don")
*/
private $donOeuvres;
Oeuvre.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation\Groups;
use App\Repository\OeuvreRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:Oeuvre:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:Oeuvre"
* }
* },
* collectionOperations={
* "get",
* "post" = {
* "denormalization_context"={
* "groups"={
* "write:Oeuvre"
* }
* }
* }
* },
* itemOperations={
* "get",
* "patch" = {
* "denormalization_context"={
* "groups"={
* "update:Oeuvre:patch"
* }
* }
* },
* "delete"
* }
* )
* #ORM\Entity(repositoryClass=OeuvreRepository::class)
*/
class Oeuvre
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups("read:Oeuvre:collection")
*/
private $id;
...
/**
* #ORM\OneToMany(targetEntity=DonOeuvre::class, mappedBy="oeuvre")
* #Groups({"write:Oeuvre"})
*/
private $donOeuvres;
and DonOeuvre.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\DonOeuvreRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={
* "groups"={
* "read:DonOeuvre:collection"
* }
* },
* denormalizationContext={
* "groups"={
* "write:DonOeuvre"
* }
* },
* collectionOperations={
* "get", "post"
* },
* itemOperations={
* "get", "patch", "delete"
* }
* )
* #ORM\Entity(repositoryClass=DonOeuvreRepository::class)
*/
class DonOeuvre
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read:DonOeuvre:collection", "read:Don:item"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Don::class, inversedBy="donOeuvres")
* #ORM\JoinColumn(nullable=false)
* #Groups({"read:DonOeuvre:collection", "write:DonOeuvre", "write:Oeuvre"})
*/
private $don;
/**
* #ORM\ManyToOne(targetEntity=Oeuvre::class, inversedBy="donOeuvres", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
* #Groups({"read:DonOeuvre:collection", "write:DonOeuvre", "read:Don:item"})
*/
private $oeuvre;

Related

How to proper relate(group) Entities in Symfony and api-platform

Im getting the error:
Nested documents for attribute "players" are not allowed. Use IRIs instead.
So, I understand that I need to make groups with a serializer.
What im working with; I use symfony 5, doctrine(SQLite) and api-platform.
Im making a game and a game needs players. So when I add a game in the DB I also want to add players. Both have their own entity; Game.php and Player.php (Also both in the entity folder).
For some reason I cant proper link 2 entities, what am i doing wrong?
I use API-platform to execute this json;
{
"active": 0,
"hints": 0,
"players": [
{
"game": 1,
"nickname": "string",
"code": "string"
}
],
"price": "345",
"gameMap": "/api/game_maps/2",
"uidGame": "4",
"teamName": "3",
"secretKey": "fdgfdg",
"startTime": "2021-03-11T11:38:45.923Z",
"lastActionOnTime": "2021-03-11T11:38:45.923Z",
"endTime": "2021-03-11T11:38:45.923Z",
"penaltyTime": "g",
"testGame": 0
}
The Game entity:
<?php
namespace App\Entity;
use App\Repository\GameRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(normalizationContext={ "groups": {"boost"} })
* #ORM\Entity(repositoryClass=GameRepository::class)
*/
class Game
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"boost"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=GameMap::class, inversedBy="games", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
* #Groups({"boost"})
*/
private $game_map;
/**
* #ORM\Column(type="string", length=255)
*/
private $uid_game;
/**
* #ORM\Column(type="string", length=255)
*/
private $team_name;
/**
* #ORM\Column(type="string", length=255)
*/
private $secret_key;
/**
* #ORM\Column(type="datetime")
*/
private $start_time;
/**
* #ORM\Column(type="datetime")
*/
private $last_action_on_time;
/**
* #ORM\Column(type="smallint")
*/
private $active;
/**
* #ORM\Column(type="datetime")
*/
private $end_time;
/**
* #ORM\Column(type="integer")
*/
private $hints;
/**
* #ORM\Column(type="bigint")
*/
private $penalty_time;
/**
* #ORM\Column(type="json")
*/
private $progress = [];
/**
* #ORM\Column(type="string", length=255)
*/
private $price;
/**
* #ORM\Column(type="smallint")
*/
private $test_game;
/**
* #ORM\OneToMany(targetEntity=Player::class, mappedBy="game", orphanRemoval=true, cascade={"persist"})
* #Groups({"boost"})
*/
private $players;
/**
* #ORM\OneToMany(targetEntity=UserGame::class, mappedBy="game", orphanRemoval=true, cascade={"persist"})
* #Groups({"boost"})
*/
private $userGames;
}
And the Player entity:
<?php
namespace App\Entity;
use App\Repository\PlayerRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=PlayerRepository::class)
*/
class Player
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"boost"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Game::class, inversedBy="players")
* #ORM\JoinColumn(nullable=false)
* #Groups({"boost"})
*/
private $game;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"boost"})
*/
private $nickname;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"boost"})
*/
private $code;
}
So, there are two different issues in your case.
Use different normalization and denormalization contexts. Add all needed fields in both Game and Player into the denormalization group:
/**
* #ApiResource(
* normalizationContext={ "groups": {"boost"} },
* denormalizationContext={ "groups": {"write"} }
* )
* #ORM\Entity()
*/
class Game
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"boost"})
*/
private $id;
...
/**
* #ORM\OneToMany(targetEntity=Player::class, mappedBy="game",
orphanRemoval=true, cascade={"persist"})
* #Groups({"boost", "write"})
*/
private $players;
}
And:
/**
* #ApiResource()
* #ORM\Entity()
*/
class Player
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*
* #Groups({"boost"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Game::class, inversedBy="players")
* #ORM\JoinColumn(nullable=false)
*/
private $game;
/**
* #ORM\Column(type="string", length=255)
*
* #Groups({"boost", "write"})
*/
private $nickname;
/**
* #ORM\Column(type="string", length=255)
*
* #Groups({"boost", "write"})
*/
private $code;
In your POST /api/games you use game: 1 in body request, in players nested collection. It's bad because you run create game requests actually. How in this case a player can be linked to an already existed game? The relationship between Game and Player is OneToMany. Do you see what I mean?
After these changes and POST /api/games the body
{
"active": 0,
"hints": 0,
"players": [
{
"nickname": "string",
"code": "string"
}
],
"price": "345",
"uidGame": "4",
"teamName": "3",
"secretKey": "fdgfdg",
"startTime": "2021-03-11T11:38:45.923Z",
"lastActionOnTime": "2021-03-11T11:38:45.923Z",
"endTime": "2021-03-11T11:38:45.923Z",
"penaltyTime": "9",
"testGame": 0
}
I got the response on GET /api/games:
[
{
"id": 1,
"uidGame": "4",
"teamName": "3",
"secretKey": "fdgfdg",
"startTime": "2021-03-11T11:38:45+00:00",
"lastActionOnTime": "2021-03-11T11:38:45+00:00",
"active": 0,
"endTime": "2021-03-11T11:38:45+00:00",
"hints": 0,
"penaltyTime": "9",
"progress": [],
"price": "345",
"testGame": 0,
"players": [
{
"id": 1,
"nickname": "string",
"code": "string"
}
]
}
]
Is it expected for you?

API not returning auto generated ID

Am working on Symfony API Platform, to add and retrieve something. Table has two fields id and title.
But when i run the GET query the API is returning title only not id.
How to return ID too?
My Annotation:-
* #ORM\Table(
* name="school",
* #ApiResource(
* attributes={
* "order"={"title": "ASC"},
* "normalization_context"={"groups"={"school.read"},
"enable_max_depth"=true},
* },
* itemOperations={
* "get",
* "put"
* },
* collectionOperations={
* "get"={
* "normalization_context"={
* "groups"={"school.read"}
* }
* }
* },
* normalizationContext={
* "groups"={"school.read"}
* },
* denormalizationContext={
* "groups"={"school.write"}
* }
* )
* #ORM\Entity(repositoryClass="Eqsgroup\Repository\SchoolRepository")
* #UniqueEntity(
* "title",
* repositoryMethod="findByUniqueCriteria",
* message="School already exists."
* )
*/
This is the Entity class
class School
{
/**
* #var string the id of this School
*
* #ORM\Id
* #ORM\Column(type="guid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
* #Groups({"school.read, school.write"})
*/
private $id;
/**
* #var string The title of the school
*
* #ORM\Column(type="string", length=255)
* #Assert\NotNull(message="school should not be empty")
* #Assert\NotBlank(message="school should not be empty")
* #Assert\Length(
* min = 1,
* max = 250,
* minMessage = "length.min,{{ limit }}",
* maxMessage = "length.max,{{ limit }}"
* )
* #Groups({"school.read", "school.write"})
*/
private $title;
public function __construct(){ }
public function getId(): ?string
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
This is the output am currently getting:
[
{
"title": "Test"
},
{
"title": "Test2"
},
]
The expected output will include the auto generated is along with title.
Add the #Groups to allow Apli-Platform to read each field you want like this :
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"school.read", "school.write"})
*/
private $id;
Have a look to the documentation here :
https://api-platform.com/docs/core/serialization/#the-serialization-context-groups-and-relations
Did you configure which attributes you want to expose ?
If not, configure it in the yaml of your entity :
# I don't know the path to your entity, just modify what you need to
XXXX\XXXX\XXXX\XXXX\School:
exclusion_policy: ALL
properties:
id:
expose: true
title:
expose: true

Doctrine2 cascade create

Here some context information: I'm building a Symfony2 application with Doctrine2 and FOSRestBundle.
My problem: I want to be able to create a parent with his children with just one JSON and one database access.
My JSON looks like this:
{
"name": "TEST_NAME",
"info": "TEST_INFO",
"cmts": [
{
"cmt": "CMT1",
"info": "INFO1"
},
{
"cmt": "CMT2",
"info": "INFO2"
},
{
"cmt": "CMT3",
"info": "INFO3"
}
]
}
Here is my TEST entity:
<?php
namespace App\Bundle\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Test
*
* #ORM\Table(name="Test")
* #ORM\Entity(repositoryClass="App\Bundle\DemoBundle\Entity\TestRepository")
*/
class Test
{
/**
* #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)
* #Assert\NotBlank()
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="info", type="string", length=255, nullable=true)
*/
private $info;
/**
* #ORM\OneToMany(targetEntity="TestCmt", mappedBy="test", fetch="EAGER", orphanRemoval=true, cascade={"merge", "remove", "persist"})
*/
protected $cmts;
/**
* Constructor
*/
public function __construct()
{
$this->cmts = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add cmts
*
* #param \App\Bundle\DemoBundle\Entity\TestCmt $cmts
* #return Test
*/
public function addCmt(\App\Bundle\DemoBundle\Entity\TestCmt $cmts)
{
$this->cmts[] = $cmts;
return $this;
}
/**
* Remove cmts
*
* #param \App\Bundle\DemoBundle\Entity\TestCmt $cmts
*/
public function removeCmt(\App\Bundle\DemoBundle\Entity\TestCmt $cmts)
{
$this->cmts->removeElement($cmts);
}
/**
* Get cmts
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCmts()
{
return $this->cmts;
}
// other getters/setters...
}
And my TESTCMT entity:
<?php
namespace App\Bundle\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* TestCmt
*
* #ORM\Table(name="TestCmt")
* #ORM\Entity(repositoryClass="App\Bundle\DemoBundle\Entity\TestCmtRepository")
*/
class TestCmt
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="cmt", type="string", length=255)
*/
private $cmt;
/**
* #var string
*
* #ORM\Column(name="info", type="string", length=255, nullable=true)
*/
private $info;
/**
* #var Test
*
* #ORM\ManyToOne(targetEntity="Test", inversedBy="cmts")
* #ORM\JoinColumn(name="test_id", referencedColumnName="id")
**/
private $test;
/**
* Set test
*
* #param \App\Bundle\DemoBundle\Entity\Test $test
* #return TestCmt
*/
public function setTest(\App\Bundle\DemoBundle\Entity\Test $test = null)
{
$this->test = $test;
return $this;
}
/**
* Get test
*
* #return \App\Bundle\DemoBundle\Entity\Test
*/
public function getTest()
{
return $this->test;
}
}
And finaly my postTestAction():
public function postTestAction(Request $request)
{
$entity = $this->deserialize($request, 'App\DemoBundle\Entity\Test');
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $entity;
}
When I send the JSON, TEST and TESTCMTs are created. Nevertheless, all "test_id" from the created TESTCMTs are "null"... And that's my problem!
EDIT: with SQL Server Profiler, I can see that Doctrine make that Transact SQL request:
INSERT INTO TESTCMT (test_id, cmt, info) VALUES (null, 'CMT', 'INFO')
I don't know why Doctrine can't send the test_id... TEST is created before TESTCMT, so "test_id" should be reachable for Doctrine to create the associate TESTCMTs.
Can someone helped me to fix it? :)
Remove #ORM\GeneratedValue(strategy="AUTO") and it won't let the DB generate a new id for the Entity

Erreur in Installation PUGXMultiUserBundle

I want to install PUGXMultiUserBundle. I followed the documentation of PUGXMultiUserBundle:https://github.com/PUGX/PUGXMultiUserBundle/blob/master/Resources/doc/index.md
I have downloaded the PUGXMultiUserBundle with success and I continued Creating my Entities. I created an entity User:
<?php
namespace Register\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="user")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"societe" = "Societe", "chercheur" = "Chercheur"})
*
*/
abstract class User extends BaseUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $image
* #ORM\Column(name="image", type="string", length=255)
*/
protected $image;
public $file;
protected function getUploadDir()
{
return 'image';
}
protected function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir()."/".$this->getId()."/";
}
public function getWebPath()
{
return null === $this->image ? null : $this->getUploadDir().$this->getId()."/".'/'.$this->image;
}
public function getAbsolutePath()
{
return null === $this->image ? null : $this->getUploadRootDir().$this->getId()."/".'/'.$this->image;
}
public function getAbsolutePath1()
{
return null === $this->image ? null : $this->getUploadRootDir().$this->image;
}
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$this->image = uniqid().'.'.$this->file->guessExtension();
}
}
/**
* #ORM\PostPersist
* #ORM\PostUpdate
*/
public function upload()
{
if (null === $this->file) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->file->move($this->getUploadRootDir(), $this->image);
unset($this->file);
}
/**
* #ORM\PreRemove()
*/
public function storeFilenameForRemove()
{
$this->filenameForRemove = $this->getAbsolutePath1();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($this->filenameForRemove) {
unlink($this->filenameForRemove);
}
}
}
and the other Entity: societe:
<?php
namespace Register\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use PUGX\MultiUserBundle\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #ORM\Table(name="societe")
* #UniqueEntity(fields = "username", targetClass = "Register\UserBundle\Entity\User", message="fos_user.username.already_used")
* #UniqueEntity(fields = "email", targetClass = "Register\UserBundle\Entity\User", message="fos_user.email.already_used")
*/
class Societe extends User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $adressemail
*/
protected $adressemail;
/**
* #var string $namesociete
*/
protected $namesociete;
}
an finally the last Entity chercheur:
<?php
namespace Register\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use PUGX\MultiUserBundle\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="chercheur")
* #UniqueEntity(fields = "username", targetClass = "Register\UserBundle\Entity\User", message="fos_user.username.already_used")
* #UniqueEntity(fields = "email", targetClass = "Register\UserBundle\Entity\User", message="fos_user.email.already_used")
*/
class Chercheur extends User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Assert\NotBlank
* #var string
*/
protected $region;
/**
* #Assert\NotBlank
* #var string
*/
protected $firstname;
}
The problem is when I want to generate my entities after : php app/console doctrine:generate:entities Register, a error message appeared in console:
[Doctrine\ORM\Mapping\MappingException]
Class "Register" is not a valid entity or mapped super class.
doctrine:generate:entities [--path="..."] [--no-backup] name
.
Can someone help me to fix this problem and Thank you.

Internalisation Symfony, Gedmo Translatable, mising translations for related entities

Im using Gedmo Translatable in my project.
I have Product entity and Inclusion entity.
Relation between them is ManyToMany.
Product Entity
namespace Traffic\ShopBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Traffic\ShopBundle\Model\Product as ProductModel;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ORM\Entity(repositoryClass="Traffic\ShopBundle\Repository\ProductRepository")
* #Gedmo\TranslationEntity(class="Traffic\ShopBundle\Entity\ProductTranslation")
*
*/
class Product extends ProductModel {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
* #Gedmo\Translatable
*
* #var type string
*/
protected $name;
/**
* #ORM\ManyToMany(targetEntity="Inclusion")
* #ORM\JoinTable(name="product_inclusion",
* joinColumns={#ORM\JoinColumn(name="product_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="inclusion_id", referencedColumnName="id")}
* )
*
* #var type Collection
*/
protected $inclusions;
/**
* #ORM\OneToMany(
* targetEntity="ProductTranslation",
* mappedBy="object",
* cascade={"persist", "remove"}
* )
*/
protected $translations;
.....
}
Inclusion Entity
namespace Traffic\ShopBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Traffic\ShopBundle\Model\Inclusion as InclusionModel;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ORM\Entity(repositoryClass="Traffic\AdminBundle\Repository\TranslatableRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"sauce" = "Sauce", "topping" = "Topping"})
* #Gedmo\SoftDeleteable(fieldName="deletedAt")
* #Gedmo\TranslationEntity(class="Traffic\ShopBundle\Entity\InclusionTranslation")
*
* #ORM\Table(name="inclusion")
*/
class Inclusion extends InclusionModel {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
* #Gedmo\Translatable
*
* #var type string
*/
protected $name;
/**
* #ORM\OneToMany(
* targetEntity="InclusionTranslation",
* mappedBy="object",
* cascade={"persist", "remove"}
* )
*/
protected $translations;
.......
}
In my Repository class I have a method to fetch translated object, but it just translates my Product not Inclusions
namespace Traffic\ShopBundle\Repository;
use Traffic\AdminBundle\Repository\TranslatableRepository;
use Traffic\ShopBundle\Entity\Kiosk;
/**
* Description of FinancialTransactionRepository
*
* #author bart
*/
class ProductRepository extends TranslatableRepository {
public function findAllProductsForKiosk(Kiosk $kiosk, $locale = "es"){
$qb = $this->createQueryBuilder("p")
->leftJoin('p.kiosks', 'k')
->leftJoin('p.flavours', 'f')
->leftJoin('p.inclusions', "i")
->leftJoin('p.type', "t")
->where('k.kiosk = :kiosk')
;
$qb->setParameter("kiosk", $kiosk);
$results = $this->getTranslatedQuery($qb, $locale);
return $results->execute();
}
}
and getTranslatedQuery
/**
* Returns translated Doctrine query instance
*
* #param QueryBuilder $qb A Doctrine query builder instance
* #param string $locale A locale name
*
* #return Query
*/
protected function getTranslatedQuery(QueryBuilder $qb, $locale = null)
{
$locale = null === $locale ? $this->defaultLocale : $locale;
$query = $qb->getQuery();
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale);
return $query;
}
Is there a way to fetch all translated objects with one query?
Maybe you should change hydration mode?
$query->setHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION);
$config = $this->container->get('doctrine')->getManager()->getConfiguration();
if ($config->getCustomHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION) === null) {
$config->addCustomHydrationMode(
TranslationWalker::HYDRATE_OBJECT_TRANSLATION,
'Gedmo\\Translatable\\Hydrator\\ORM\\ObjectHydrator'
);
}

Resources