RuntimeException
File:
/var/www/verzeilberg-dev/vendor/doctrine/doctrine-module/src/Form/Element/Proxy.php:216
Message:
No object manager was set
I have a Laminas framework with doctrine.
I want to generate checkboxes based on a many to many relationship.
This is the blog entity:
/**
* Many Blogs have Many Categories.
* #ORM\ManyToMany(targetEntity="Category", inversedBy="blogs")
* #ORM\JoinTable(name="blog_category")
* #Annotation\Type("DoctrineModule\Form\Element\ObjectMultiCheckbox")
* #Annotation\Options({
* "target_class":"Blog\Entity\Category",
* "property": "name",
* "label": "Categorieënnnnnn"
* })
*/
private $categories;
This is the inversed side, category entity:
/**
* Many Categories have Many Blogs.
* #ORM\ManyToMany(targetEntity="Blog", mappedBy="categories")
*/
private $blogs;
This is the Annotationbuilder:
$builder = new AnnotationBuilder($this->entityManager);
$form = $builder->createForm($blog);
$form->setHydrator(new DoctrineHydrator($this->entityManager, Blog::class));
$form->bind($blog);
And the view:
<?php echo $this->formMultiCheckbox($form->get('categories')); ?>
Keep getting above error.
Related
So because of api-platform.com Unable to generate an IRI for the item of type I tried using a different approach and declare custom operations on my user entity for login, registration and reset (since I stil want custom business logics for them). So the initial set-up of that in api-platform is rather easy. I added the following code to my user entity
* collectionOperations={
* "register"={"route_name"="user_register","normalization_context"={"groups"={"registerRead"}},"denormalization_context"={"groups"={"registerWrite"}}},
* "reset"={"route_name"="user_reset","normalization_context"={"groups"={"resetRead"}},"denormalization_context"={"groups"={"resetWrite"}}},
* "login"={"route_name"="user_login","normalization_context"={"groups"={"loginRead"}},"denormalization_context"={"groups"={"loginWrite"}}},
* "token"={"route_name"="user_token","normalization_context"={"groups"={"tokenRead"}},"denormalization_context"={"groups"={"token"}}}
* },
And then added the appropriate actions to the user controller.
/**
* #Route(
* name="user_login",
* path="api/user/login",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="login",
* "_api_receive"=false
* }
* )
*/
public function loginAction(User $data): User {
///$this->userService->login($data);
return $data;
}
/**
* #Route(
* name="user_register",
* path="api/user/register",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="register",
* "_api_receive"=false
* }
* )
*/
public function registerAction(User $data): User {
///$this->userService->register($data);
return $data;
}
/**
* #Route(
* name="user_reset",
* path="api/user/reset",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="reset",
* "_api_receive"=false
* }
* )
*/
public function resetAction(User $data): User {
//$this->userService->reset($data);
return $data;
}
/**
* #Route(
* name="user_token",
* path="api/user/token",
* methods={"POST"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_collection_operation_name"="token",
* "_api_receive"=false
* }
* )
*/
public function tokenAction(User $data): User {
//$this->userService->reset($data);
return $data;
}
So far al fine, however..... because we are using a post operation here and the user is a doctrine ORM entity the api-platform bundle atomically adds the post to the database. But I don’t want that, I want it to pass the entity on to the controller who then uses a service to do business logics. And determine if and how the post should be processed.
Now I went over the documentation and the problem seems to be that the WriteListener always triggers there were other triggers (e.g. ReadListener, DeserializeListener and ValidateListener) can be disabled trough the _api_receive parameter.
So that leaves the question is there a way to disable the WriteListener on a specific operation or route?
Kind Regards,
Ruben van der Linde
You can return an instance of HttpFoundation's Response instead of $data. Then no listener registered on kernel.view will be called.
But introducing a listener similar to api_receive for the write listener is a good idea. Would you mind opening a Pull Request?
Edit: I've opened a Pull Request to introduce this new flag: https://github.com/api-platform/core/pull/2072
I'm using the Symfony 3 Framework with Doctrine and MongoDB.
I've two documents that are in an OneToMany relationship.
/**
* Class Waypoint
* #package AppBundle\Document
* #MongoDB\Document(collection="waypoints", repositoryClass="AppBundle\Repository\WaypointRepository")
*/
class Waypoint
{
/**
* #var int
*
* #MongoDB\Id(strategy="auto")
*/
private $id;
/**
* #var ArrayCollection
* #MongoDB\ReferenceMany(targetDocument="Comment", cascade={"delete"})
*/
private $comments;
}
**
* Class Comment
* #package AppBundle\Document
* #MongoDB\Document(collection="comments", repositoryClass="AppBundle\Repository\CommentRepository")
*/
class Comment
{
/**
* #var int
*
* #MongoDB\Id(strategy="auto")
*/
private $id;
/**
* #var Waypoint
*
* #MongoDB\ReferenceOne(targetDocument="Waypoint", inversedBy="comments")
* #Assert\NotBlank()
*/
private $waypoint;
}
Now I'm getting a part of my Waypoint entries from an repository query and want to display them with twig.
/**
* WaypointRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class WaypointRepository extends DocumentRepository
{
public function getWaypointsForCruiseByPage(Cruise $cruise, $page)
{
$displayLimit = 10;
$amountToSkip = 0;
if ($page > 1)
{
$amountToSkip = ($page -1) * $displayLimit;
}
$qb = $this->createQueryBuilder()
->select()
->field('cruise')->equals($cruise)
->field('isAutoWaypoint')->equals(false)
->sort('date', -1)
->skip($amountToSkip)
->limit($displayLimit)
;
$qb
->addOr($qb->expr()->field('hasImage')->equals(true))
->addOr($qb->expr()->field('hasAudio')->equals(true))
->addOr($qb->expr()->field('description')->notEqual(''))
;
return $qb->getQuery()->toArray();
}
}
Now, I'm trying to do {{ waypoint.comments.count }} or {{ waypoint.comments|length }} will always be 0, even if I'm having datasets in my MongoDB collection.
If I'm getting the comments over the CommentRepository by the ID of the Waypoint I'm getting the expected results.
// returns the expected results
public function getAllCommentsForWaypoint(Waypoint $waypoint)
{
return $this->createQueryBuilder()
->select()
->field('waypoint')->equals($waypoint)
->getQuery()->toArray()
;
}
The mapping is fine as far as I can tell, no flaws or errors to find.
Why is the PersistentCollection empty, event though informations are there in the collection?
I'm not sure how are you creating documents, but this is my best shot:
Waypoint::$comments is not mapped as an inverse side thus ODM expects list of references to be available in the waypoint.comments field in the database. Most probably it's not there (i.e. you're not explicitly adding new Comment to the collection in the Waypoint) and that's why you're seeing empty collection when querying for waypoints, but have results when querying for comments. Given you have inversedBy="comments" in the Comment mapping I think you forgot to set the Waypoint::$comments as the inverse side:
/**
* #var ArrayCollection
* #MongoDB\ReferenceMany(targetDocument="Comment", mappedBy="waypoint")
*/
private $comments;
I have three entities like following:
1. Customer.php
<?php
//...
/**
* Customer
*
* #ORM\Table(name="customers")
* #ORM\Entity(repositoryClass="CompanyBundle\Repository\CustomerRepository")
* #ORM\HasLifecycleCallbacks
*/
class Customer
{
/**
* #ORM\OneToMany(targetEntity="CustomerAddress", mappedBy="customer")
*/
private $customerAddresses;
// ...
}
?>
2. CustomerAddress.php
<?php
//...
/**
* CustomerAddress
*
* #ORM\Table(name="customer_address")
* #ORM\Entity(repositoryClass="CompanyBundle\Repository\CustomerAddressRepository")
*/
class CustomerAddress
{
/**
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="customerAddresses")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $customer;
/**
* #ORM\ManyToOne(targetEntity="CustomerAddressType", inversedBy="customerAddresses")
* #ORM\JoinColumn(name="customer_address_type_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $customerAddressType;
//...
}
3. CustomerAddressType.php
<?php
//...
/**
* CustomerAddressType
*
* #ORM\Table(name="customer_address_type")
* #ORM\Entity(repositoryClass="CompanyBundle\Repository\CustomerAddressTypeRepository")
*/
class CustomerAddressType
{
/**
* #ORM\OneToMany(targetEntity="CustomerAddress", mappedBy="customerAddressType")
*/
private $customerAddresses;
//...
}
Here are the rows from table customer_address_type
I want to get all customer addresses of type either 'BA' or 'SA". So I want to remove all other type except these two. Basically I want to do something similiar like query scope in Laravel.
foreach ($customers as $customer) {
// Here I want to filter customer addresses
// Currently its giving me all
$customer_address = $customer->getCustomerAddresses();
}
Is it possible to do like so without using custom query?
You can get an ArrayCollection with all the addresses and then use filter method to get only the ones that you want. You can do it inside the Customer entity so you can reuse it for serialization.
You have to pass a closure to the filter method, and then it iterates over the collection evaluating the closure that should renturn true when you want to include the item in the result or false if not.
I have a page address.html.twig , the user can add many addresses in the table UserAddress. when he added his address in the database , the address should be render in the same page that he added his address then he can choose which one he would like to use. Unfortunately the address is not render.
First i thought that i have a problem in my controller action or in my twig page. I even asked a question here about it => here
I verified all my tables in phpmyadmin and all of them are well link but if i'm doing this: php app/console doctrine:schema:validate
i have this error :
[Mapping] FAIL - The entity-class
'FLY\BookingsBundle\Entity\Commandes' mapping is invalid:
* The association FLY\BookingsBundle\Entity\Commandes#user refers to the inverse side field
Application\Sonata\UserBundle\Entity\User#commandes which does not
exist.
[Mapping] FAIL - The entity-class
'FLY\BookingsBundle\Entity\UserAddress' mapping is invalid:
* The association FLY\BookingsBundle\Entity\UserAddress#user refers to the inverse side field
Application\Sonata\UserBundle\Entity\User#address which does not
exist.
Have a look at this picture:
This is my UserAddress.php
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="address")
* #ORM\JoinColumn(nullable=true)
*/
private $user;
Commandes.php
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="commandes")
* #ORM\JoinColumn(nullable=true)
*/
private $user;
User.php
/**
* #ORM\Entity(repositoryClass="FLY\UserBundle\Repository\UserRepository")
* #ORM\Table(name="fos_user_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
$this->commandes = new \Doctrine\Common\Collections\ArrayCollection();
$this->address = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #ORM\OneToMany(targetEntity="FLY\BookingsBundle\Entity\Commandes", mappedBy="user", cascade={"remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $commandes;
/**
* #ORM\OneToMany(targetEntity="FLY\BookingsBundle\Entity\UserAddress", mappedBy="user", cascade={"remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $address;
Here you can see my var dump:
User {#124 ▼
#id: 21
-commandes: null
-address: null
}
I've had an issue which has popped up 2-3 times in the last few years, where the mappings were incorrect but the schema update was successful. After the mappings were fixed this wasn't reflected in the schema and symfony assumed it was already up-to-date.
I recommend you try removing the relevent relationships manually from your user, commande and address tables and then run:
php app/console doctrine:schema:update --force
- it may fix your issue.
Heres an example from one of my apps - I've done this for your commandes entity.
You'll be able to piece together your UserAddress Entity from this example yourself!
Here goes:
User.php
/**
* #ORM\OneToMany(targetEntity="FLY\BookingsBundle\Entity\Commandes", mappedBy="commandesUser")
*/
protected $commandes;
User.php - Getters and Setters
/**
* Add commandes
*
* #param FLY\BookingsBundle\Entity\Commandes $commandes
*/
public function addCommandes(\FLY\BookingsBundle\Entity\Commandes $commandes)
{
$this->commandes[] = $commandes;
}
/**
* Get commandes
*
* #return Doctrine\Common\Collections\Collection
*/
public function getCommandes()
{
return $this->commandes;
}
Commandes.php
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="commandes")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
private $commandesUser;
Commandes.php - Getters and Setters
/**
* Set commandesUser
*
* #param Application\Sonata\UserBundle\Entity\User $commandesUser
*/
public function setCommandesUser(\Application\Sonata\UserBundle\Entity\User $commandesUser = null)
{
$this->commandesUser = $commandesUser;
}
/**
* Get $commandesUser
*
* #return Application\Sonata\UserBundle\Entity\User
*/
public function getCommandesUser()
{
return $this->commandesUser;
}
It's quite likely this doesn't happen to anyone else, but there's a chance.
In my case, this error appeared because there was a duplicate. My entity had 2 fields, which are ManyToOne relationships. And they both had the same inversed names, which gave this error.
So this is the relevant bit of code:
class TaskIngredient
{
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Ingredient", inversedBy="taskIngredients")
* #ORM\JoinColumn(nullable=false)
*/
private $ingredient;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Task", inversedBy="taskIngredients")
* #ORM\JoinColumn(nullable=false)
*/
private $task;
}
The solution was relatively easy. I tried changing the inversedBy name, manually. However this didn't fix it (even after applying php app/console doctrine:schema:update --force and removing the var/cache folder).
So I just:
Removed one of the problematic entities (and it's setter/getter)
Ran the php bin/console make:entity tool and readded the field with a different name
Voilà! Issue fixed.
I'm working on some kind of "complex" form in my project where entities are persisted on each steps since individual forms are split on them. Then I've a first step (lets call it step1) where I persist a entity and also store it on the session, see code below:
$productoSolicitudEntity = new Entity\ProductoSolicitud();
$productoSolicitudForm = $this->createForm(new Form\ProductoSolicitudForm(), $productoSolicitudEntity);
$productoSolicitudForm->handleRequest($request);
if ($productoSolicitudForm->isValid()) {
$productoSolicitudRequest = $request->get('productoSolicitud');
try {
$producto = $em->getRepository("AppBundle:Producto")->find($productoSolicitudRequest['producto']['nombre']);
$productoSolicitudEntity->setProducto($producto);
$condicionProducto = $em->getRepository("AppBundle:CondicionProducto")->find($productoSolicitudRequest['condicion_producto']);
$productoSolicitudEntity->setCondicionProducto($condicionProducto);
$finalidadProducto = $em->getRepository("AppBundle:FinalidadProducto")->find($productoSolicitudRequest['finalidad_producto']);
$productoSolicitudEntity->setFinalidadProducto($finalidadProducto);
$procedenciaProducto = $em->getRepository("AppBundle:ProcedenciaProducto")->find($productoSolicitudRequest['procedencia_producto']);
$productoSolicitudEntity->setProcedenciaProducto($procedenciaProducto);
$productoSolicitudEntity->setLote($productoSolicitudRequest['lote']);
$solicitudUsuario = $em->getRepository("AppBundle:SolicitudUsuario")->find($session->get('solicitudUsuarioEntity')->getId());
$productoSolicitudEntity->setSolicitudUsuario($solicitudUsuario);
$em->persist($productoSolicitudEntity);
$em->flush();
$session->set('productoSolicitudEntity', $productoSolicitudEntity);
$response['success'] = true;
} catch (Exception $ex) {
$status = 400;
$response['error'] = $ex->getMessage();
}
} else {
$status = 400;
$response['error'] = $this->get('translator')->trans('formularioNoValido');
$response['formError'] = $this->getFormErrors($productoSolicitudForm);
}
Then in the four step (lets call it step4) I need to attach that entity to a new one since they are related and this is the code involve:
$productoSolicitud = $session->get('productoSolicitudEntity');
if (! $productoSolicitud) {
$status = 400;
$response['error'] = 'No se encontró la solicitud';
}
$distribuidorEntity = new Entity\FabricanteDistribuidor();
$distribuidorForm = $this->createForm(new Form\DistribuidorForm(), $distribuidorEntity);
$distribuidorForm->handleRequest($request);
if ($distribuidorForm->isValid()) {
$em->persist($distribuidorEntity);
$em->flush();
$session->set('distribuidorEntity', $distribuidorEntity);
$distribuidorProductoSolicitudEntity = new Entity\DistribuidorProductoSolicitud();
$distribuidorProductoSolicitudEntity->setProductoSolicitud($productoSolicitud);
$distribuidorProductoSolicitudEntity->setFabricanteDistribuidor($distribuidorEntity);
$em->persist($distribuidorProductoSolicitudEntity);
$em->flush();
$session->set('distribuidorEntity', $distribuidorEntity);
}
But I'm getting this error:
A new entity was found through the relationship 'AppBundle\Entity\DistribuidorProductoSolicitud#producto_solicitud' that was not configured to cascade persist operations for entity:
AppBundle\Entity\ProductoSolicitud#000000000a1f3e9d00007f88c54033f8. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example
#ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'AppBundle\Entity\ProductoSolicitud#__toString()' to get a clue.
Since the conflictive entity seems to be DistribuidorProductoSolicitud then I made this change on it:
/**
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\ProductoSolicitud", cascade={"persist"})
* #ORM\JoinColumn(name="producto_solicitud_id", referencedColumnName="id")
*/
protected $producto_solicitud;
But does not solve the issue, any help? What's is wrong? What I'm missing here? I should add a method __toString() at ProductoSolicitud entity but what this should return?
This are the entities involved on the issue:
class DistribuidorProductoSolicitud
{
use IdentifierAutogeneratedEntityTrait;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\FabricanteDistribuidor")
* #ORM\JoinColumn(name="fabricante_distribuidor_id", referencedColumnName="id")
*/
protected $fabricante_distribuidor;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\ProductoSolicitud", cascade={"persist"})
* #ORM\JoinColumn(name="producto_solicitud_id", referencedColumnName="id")
*/
protected $producto_solicitud;
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Pais", inversedBy="distribuidorProductoSolicitudPais", cascade={"persist"})
* #ORM\JoinTable(name="nomencladores.pais_distribuidor_producto_solicitud", schema="nomencladores",
* joinColumns={#ORM\JoinColumn(name="distribuidor_producto_solicitud_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="pais_id", referencedColumnName="id")}
* )
*/
protected $paisesDistribuidorProductoSolicitudPais;
}
class ProductoSolicitud
{
use IdentifierAutogeneratedEntityTrait;
/**
* #var \Producto
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Producto")
* #ORM\JoinColumn(name="producto_id", referencedColumnName="id")
*/
protected $producto;
/**
* #var \SolicitudUsuario
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\SolicitudUsuario", cascade={"persist"})
* #ORM\JoinColumn(name="solicitud_usuario_id", referencedColumnName="id")
*/
protected $solicitud_usuario;
/**
* #var \CondicionProducto
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\CondicionProducto")
* #ORM\JoinColumn(name="condicion_producto_id", referencedColumnName="id")
*/
protected $condicion_producto;
/**
* #var \FinalidadProducto
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\FinalidadProducto")
* #ORM\JoinColumn(name="finalidad_producto_id", referencedColumnName="id")
*/
protected $finalidad_producto;
/**
* #ORM\Column(name="lote", type="integer", nullable=false)
*/
protected $lote;
/**
* #var \ProcedenciaProducto
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\ProcedenciaProducto")
* #ORM\JoinColumn(name="procedencia_producto_id", referencedColumnName="id")
*/
protected $procedencia_producto;
}
Where the cascade={"persist"} should go in order to fix it?
I've found this post but it's no helpful.
Saving (or serializing) a Doctrine entity to the session is problematic (here's a relevant SO question/answer on the matter) - since it loses the private properties that are needed to detect a hydrated Doctrine object that can be recognized in the system.
Since those private properties of a hydrated Doctrine object are missing, it perceives these unhydrated objects as entirely new (and the other associated objects.)
Your best solution is to only store the Object identifier in the session and retrieve them later with the find() helper function.
To store:
$this->get('session')->set('objectId', $object->getId());
To fetch later:
$objectId = $this->get('session')->get('objectId');
$object = $this->getDoctrine()->getRepository('AcmeBundle:Entity')->find($objectId);
Try to add cascade={"persist"} to both sides of your ManyToOne (in ProductoSolicitud and DistribuidorProductoSolicitud).
If this ManyToOne is unidirectional, try to change it to a OneToMany bidirectional with cascade persist on both sides.
In class ProductoSolicitud:
/**
* #OneToMany(targetEntity="AppBundle\Entity\DistribuidorProductoSolicitud", mappedBy="producto_solicitud", cascade={"persist"})
#var \Doctrine\Common\Collections\ArrayCollection
**/
private $distribuidor_producto_solicidudes;
In class DistribuidorProductoSolicidud:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\ProductoSolicitud", inversedBy="distribuidor_producto_solicidudes", cascade={"persist"})
* #ORM\JoinColumn(name="producto_solicitud_id", referencedColumnName="id")
#var \AppBundle\Entity\ProductoSolicidud
*/
protected $producto_solicitud;