Symfony 3 - Add an array entities to the database from entity? - php

I would like to add a field of type array to my database, and be able to use it, as does FOSUserBundle with the roles.
The goal is to add a table of packages to my User entity.
I don't want a join table.
I reproduced the same schema as for the roles and I added all this to my User entity
Some code of my User.php entity :
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
* #var array
*
* #ORM\Column(name="packages", type="array", length=255)
*/
private $packages;
public function addPackage($package)
{
if (!in_array($package, $this->packages, true)) {
$this->packages[] = $package;
}
}
/**
* {#inheritdoc}
*
* #return array
*/
public function getPackages()
{
return $this->packages;
}
/**
* {#inheritdoc}
*/
public function setPackages(array $packages)
{
$this->packages = array();
foreach ($packages as $package) {
$this->addPackage($package);
}
return $this;
}
/**
* {#inheritdoc}
*/
public function removePackage($package)
{
if (false !== $key = array_search(strtoupper($package), $this->packages, true)) {
unset($this->packages[$key]);
$this->packages = array_values($this->packages);
}
return $this;
}
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->packages = array();
}
In my database, the packages field is present, and seems to be like the roles field. But when I connect with my user on the site, I have this error:
Could not convert database value "" to Doctrine Type array
Someone would know what's wrong with the code?
My Database screenshot :
Database structure
Database view

If you don't want to set type="string" in your $packages, you can keep the "array" and do this sql command, and add nullable=true for the other values that you already have in your BD: Like this, the empty values will be converted to null.
#ORM\Column(type="array", nullable=true)
and do sql command:
ALTER TABLE my_table ADD my_new_column LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)'
or by symfony console:
php bin/console doctrine:query:sql "UPDATE <<your_table_name>> SET <<field_name>> = NULL WHERE <<field_name>>=''"

Related

Symfony : how to construct the good relation between my entities?

I'm a new user of Symfony, and I want to make a relation between 2 entities.
I have an entity named 'Suggest' and the other one 'Car'
Each suggest can have one or many cars, and a car can be used to 0 or multiple suggest
I want a new field named 'Cars' ( field for the name of cars) in my 'Suggest' entity, but I don't want new field in 'Car' entity
How can I do it ?
I tried to make a relation ManyToOne on my Suggest entity but it create me an id field of cars, and I can only put one.
with a ManyToMany relation, it is possible to get only a new field on my 'suggest' entity ?
Assuming you're using the make:entity command: if you add a cars property to your Suggest entity, and make it a ManyToMany relation to Car, it does not add an extra property to the Car class (you can say no to that):
Car:
<?php
namespace App\Entity;
use App\Repository\CarRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=CarRepository::class)
*/
class Car
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
}
And Suggest:
<?php
namespace App\Entity;
use App\Repository\SuggestRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=SuggestRepository::class)
*/
class Suggest
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity=Car::class)
*/
private $cars;
public function __construct()
{
$this->cars = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* #return Collection<int, Car>
*/
public function getCars(): Collection
{
return $this->cars;
}
public function addCar(Car $car): self
{
if (!$this->cars->contains($car)) {
$this->cars[] = $car;
}
return $this;
}
public function removeCar(Car $car): self
{
$this->cars->removeElement($car);
return $this;
}
}

Why is Id AutoGenerated anew on UPDATE with Relation using Symfony Doctrine?

Symfony 5.3
Doctrine bundle ^2.4 ORM ^2.9
MariaDB 10.6.4
This has been rather difficult to diagnose especially as I was dealing with some complicated layered code. If I was sure, I would have filed a bug with Doctrine, but I want to first make sure I'm not making some glaring mistake or such, in implementation.
I have painstakingly tried to reduce the code to a simplified working example. On my test database tables, there are additional columns that are not referred to in the demo code.
// src/Entity/Record.php
declare(strict_types = 1);
namespace App\Entity;
use App\Repository\RecordRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=RecordRepository::class)
* #ORM\Table(name="Records")
*/
class Record {
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
public function getId(): ?int {
return $this->id;
}
/**
* #ORM\OneToOne(targetEntity="App\Entity\RecordStatus", mappedBy="record", cascade={"persist"})
* #ORM\JoinColumn(name="Id")
*/
private ?RecordStatus $recordStatus = NULL;
public function getRecordStatus(): ?RecordStatus {
return $this->recordStatus;
}
public function setRecordStatus(RecordStatus $value): void {
$value->setRecord($this);
$this->recordStatus = $value;
}
}
// src/Entity/RecordStatus.php
declare(strict_types = 1);
namespace App\Entity;
use App\Repository\RecordStatusRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=RecordStatusRepository::class)
* #ORM\Table(name="Record_Statuses")
*/
class RecordStatus {
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
public function getId(): ?int {
return $this->id;
}
/**
* #ORM\OneToOne(targetEntity="App\Entity\Record", inversedBy="record")
* #ORM\JoinColumn(name="RecordId")
*/
private Record $record;
public function getRecord(): Record {
return $this->record;
}
public function setRecord(Record $value): void {
$this->record = $value;
}
}
// src/Controller/DefaultController.php
declare(strict_types = 1);
namespace App\Controller;
use App\Entity\Record;
use App\Entity\RecordStatus;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController {
/**
* #Route("/")
*/
public function home(): Response {
$doctrine = $this->getDoctrine();
$entityManager = $doctrine->getManager();
$recordRepository = $doctrine->getRepository(Record::class);
$recordStatus = new RecordStatus;
$item = $recordRepository->find(1);
$item->setRecordStatus($recordStatus);
$entityManager->flush();
return new JsonResponse(['id' => $item->getId()]);
}
}
When this route ("/") is triggered, the record is updated and the old Id is displayed. But after the update, checking the database shows that the record now has a newly Auto-Generated Id, that comes after the last previous record in the table. Note that this is on UPDATE and not INSERT.
My current partial workaround is to load the currently mapped relation if one exists and update it instead of using new RecordStatus, but it should also be possible to use a new Related instance if required (especially if there was no Relation assigned on first insert).

Doctrine always filter rows with certain column values

I am using Symfony 5.1 with doctrine. I would like to know how to put a filter on a field/column for all doctrine queries that do a search on an entity. For example, with the entity Sejour I would like to make sure all queries that search for this entity have the where by clause on the field/column: "sejAnnule != 'Y'". Here is the Sejour entity:
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* Sejour.
*
* #ORM\Table(name="Sejour")})
* #ORM\Entity(repositoryClass="App\Repository\SejourRepository")
*/
class Sejour
{
/**
* #ORM\Column(name="SEJ_NO", type="integer", nullable=false)
* #ORM\Id
*/
private int $sejNo;
/**
* #var string|null
*
* #ORM\Column(name="SEJ_ANNULE", type="string", length=1, nullable=true)
*/
private string $sejAnnule;
public function getSejAnnule(): ?string
{
return $this->sejAnnule;
}
public function setSejAnnule(?string $sejAnnule): void
{
$this->sejAnnule = $sejAnnule;
}
public function getSejNo(): int
{
return $this->sejNo;
}
public function setSejNo(int $sejNo): void
{
$this->sejNo = $sejNo;
}
}
I think this is possible with doctrine filters but I was wondering if anyone knows a quicker way to do this (e.g. an annotation on the field or a bundle)?
The easiest way to this I think is to use a doctrine filter. No need to create an event listener (as i first thought). Create the filter:
<?php
namespace App\Filter;
use App\Entity\Sejour;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
class SoftDeleteFilter extends SQLFilter
{
/**
* {#inheritdoc}
*/
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if (Sejour::class == $targetEntity->getReflectionClass()->name) {
return sprintf(' %s.SEJ_ANNULE != \'Y\'', $targetTableAlias);
}
return '';
}
}
Then enable the filter in doctrine.yaml:
orm:
filters:
soft_delete_filter:
class: AppBundle\Doctrine\SoftDeleteFilter
enabled: true
Symfony documentation: https://symfony.com/doc/current/bundles/DoctrineBundle/configuration.html#filters-configuration
The Symfony Casts documentation (although this is with an event listener) https://symfonycasts.com/screencast/doctrine-queries/filters

Updating Entity OneToMany relationship by passing arraycollection

Im trying to change an old arraycolection of $linhas for a new one by using the method
setLinhas(Arraycollection $linhas)
but what happens when it does the changes is that internally he creates a new object with the new lines and dont update the old object with the new lines. It creates a new instance with the same values as the old object. It was suppose to update the same object and not create a new one!
Entity's Property :
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\LinhasPrecos", mappedBy="preco",orphanRemoval=true,cascade={"persist","merge"})
*/
protected $linhas;
/**
* #param $linhas
*/
public function setLinhas($linhas)
{
$this->linhas = new ArrayCollection($linhas);
}
In the service:
$oldObject->setLinhas($newObectWithNewLinhas->getLinhas());
$this->em->persist($oldObject);
but if I do the change manually it will work:
$oldLinhas = $oldObject->getLinhas()->getValues();
foreach($oldLinhas as $oldLinha)
{
$oldObject->removeLinha($oldLinha);
}
$linhaToCopy = $newObectWithNewLinhas->getLinhas()->getValues();
foreach($linhasCopyNew as $linhaCopyNew)
{
$oldObject->addLinha($linhaCopyNew);
}
thanks in advance!
You are doing it wrong!
use this constructor and setter instead:
Preco
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Preco
{
//...
/**
* #var Collection
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\LinhasPrecos", mappedBy="preco", orphanRemoval=true, cascade={"persist","merge"})
*/
protected $linhas;
//...
public function __construct()
{
$this->linhas = new ArrayCollection();
}
public function setLinhas($linhas)
{
$this->linhas = $linhas;
}
}
Notice
You should pass a doctrine collection into setLinhas.
This way you are totally replacing an old collection, with the new collection (and not adding an element to the old collection).

Symfony3, Missing argument 1 for Doctrine\ORM\EntityRepository::__construct(), how and why

I'm making my first small app in symfony, a simple blog.
Now I have been using the documentation for both symfony and doctrine and want to preform a simple, beginner task: display a json encoded simple table
Yet somehow I cant seem to get along with doctrine.
Here is my data (apart form the view which does nothing but display the value):
//AppBundle/Controller/DefaultController.php
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Entity\Post;
use Doctrine\Common\Persistence;
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
$database = new Post();
$output = $database->findAll();
return $this->render('default/index.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'x' => json_encode($output)
]);
}
}
<?php
//AppBundle/Entity/Post.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\EntityRepository;
/**
* #ORM\Entity
* #ORM\Table(name="sqltest")
*/
class Post extends EntityRepository
{
//The post for now uses data from a temponary test table
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
*/
private $name;
/**
* #ORM\Column(type="integer", scale=2)
*/
private $number;
/**
* Get id
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
* #param string $name
* #return Post
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set number
* #param integer $number
* #return Post
*/
public function setNumber($number)
{
$this->number = $number;
return $this;
}
/**
* Get number
* #return integer
*/
public function getNumber()
{
return $this->number;
}
}
Problem is when I try to display the website i get this exception
Warning: Missing argument 1 for
Doctrine\ORM\EntityRepository::__construct(), called in
C:\Users\Alan\Desktop\symf-blog\src\AppBundle\Controller\DefaultController.php
on line 19 and defined
Problematic line being the one with $database = new Post();
I am very new in this and am aware that the response is very simple and I just don't see it. When answering please provide and explanation which even a dead rabbit could understand.
Pretty thanks for your patience.
PS: Also an explanation about what the $em variable I've seen so much about is for and from where do I get it would be nice
If you're want a repository class for custom DB functions then this is the right way to do it:
namespace AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
class PostRepository extends EntityRepository
{
public function findAll()
{
return $this->findBy(array(), array('id' => 'DESC', 'createdAt' => 'DESC'));
}
....
}
Then in your controller:
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository("AppBundle:Post")->findAll();
Remove the annotations (them belongs to the entity). Also pay attention to what #james_bond told you. Try that!
As stated in the documentation, you access custom repository classes through doctrine entity manager.
$em = $this->getDoctrine()->getManager();
$posts = $em->getRepository('YourBundle:Post')->findAll();
Also you're mixing your entity definition with your repository definition, which is not a good idea.
Please refer to the doctrine documentation in symfony for proper usage.

Categories