I am trying to create a saveAction in zend2 framework using doctrine.
in my PromotionsController i have this action:
public function saveLinkAction() {
$view = new ViewModel();
$salonId = (int) $this->params()->fromPost('salon_id', null);
$addLink = $this->getServiceLocator()->get('Promotions\Model\Link');
$linkData['salon_id'] = $salonId;
$linkData['link'] = '/link/example';
$addLink->setData($linkData);
return $view;
}
This is just for learning how to write data in database.
$addLink = $this->getServiceLocator()->get('Promotions\Model\Link');
This line of code is showing an error and i don't know what is the cause?
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Promotions\Model\Link
I have created a Link.php in Model directory.
<?php
namespace Link\Model;
use Application\Model\Entity;
use Zend\Form\Annotation;
/**
* #Entity
* #Table(name="promo_link")
*/
class Link extends Entity {
/********** PROPERTIES **********/
/**
* #Id #GeneratedValue(strategy="AUTO") #Column(name="id", type="integer")
* #var int
*
* #Annotation\Exclude()
*/
protected $id;
/**
* #Column(name="salon", type="integer")
* #var int
*
* #Annotation\Options({"label":"Salon"})
* #Annotation\Validator({"name": "Digits"})
*/
protected $salon;
/**
* #Column(name="link", type="string")
* #var string
*/
protected $link;
/**
* #Column(name="start_date", type="string")
* #var string
*/
protected $start_date;
/**
* #Column(name="end_date", type="string")
* #var string
*/
protected $end_date;
}
?>
The error tells you where the problem is:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Promotions\Model\Link
Meaning: The ServiceManager doesn't know what Promotions\Model\Link is supposed to be. This key either doesn't exist in your SMConfig or while creating the instance to be returned some error occurs.
TL/DR - Check your ServiceManager Configuration regarding the key Promotions\Model\Link
In order to save your data in your database, you will need the entitymanager.
$link = new Link();
$link->setSalonId($salonId);
$link->setLink('/link/example');
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$em->persist($link);
$em->flush();
You can start with the above code.
However, preferably you would create a repository and a servicelayer. The service should have access to the entityManager and hold your logic. Your controller should have access to this service.
Related
I use Loggable to backup changes in Entities.
The default AbstractLogEntry does not have enough columns for my needs.
Thats why i extended the class and added extra getters and setters.
See the code below
/**
* EmployeeBackup
*
* #ORM\Table(name="employee_backup")
* #ORM\Entity(repositoryClass="Gedmo\Loggable\Entity\Repository\LogEntryRepository")
*
*/
class EmployeeBackup extends AbstractLogEntry
{
/**
* #var int
*
* #ORM\Column(name="division_id", type="integer", unique=true)
*/
private $divisionId;
/**
* #return int
*/
public function getDivisionId(): int
{
return $this->divisionId;
}
/**
* #param string $divisionId
*/
public function setDivisionId(string $divisionId): void
{
$this->divisionId = $divisionId;
}
}
The extension is using the class above. So it works.
But now i need to set the divisionId when a new version is stored.
I tried the code below
$loggable = new LoggableListener();
$loggable->setDivision($division);
$evm->addEventSubscriber($loggable);
And this is what i get:
Attempted to call an undefined method named "setDivision" of class "Gedmo\Loggable\LoggableListener".
And thats true because LoggableListener does not have a setDivision function. My question is: Do i need to override the listener and if so, how do i do that?
Thanks ;)
Basically I create a form that uses multiple entity, and the result retrieved from this form I wish the stock in another table separated into BDD. I was told to make an insertion (or update) request in a repository and call it from the controller after checking the submitted data. But in this case there will be no persist or flush in this case since you do not save an object corresponding to an entity there will be no persist or flush something like that, but I arrive Not really have to do it.
That's why I want to store in another table:
When I validate my form, my result retrieved by my form is stocked here (id: 6)
this is my code :
public function testAction(Request $request ){
$poste = new Poste();
$formPoste = $this->get('form.factory')->create(PosteType::class, $poste );
$historique = new Historique_employer();
if ($request->isMethod('POST')&& $formPoste->handleRequest($request)->isValid()) {
$historique->setPoste($request->request['poste_id']->getData());
$historique->setCity($request->request['City']->getData());
$historique->setEmployer($request->request['Employer']->getData());
$historique->setTemps($request->request['Temps']->getData());
dump($poste);die();
$em = $this->getDoctrine()->getManager();
$em->persist($poste);
$em->persist($historique);
$em->flush();
}
return $this->render(':stat:teste.html.twig', array(
'poste' => $formPoste->createView(),
'historique' => $historique,
));
}
and I would like store my data in this entity :
class Historique_employer
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Poste
* #ORM\OneToOne(targetEntity="Poste")
*/
private $poste;
/**
* #var City
* #ORM\OneToOne(targetEntity="City")
*/
private $city;
/**
* #var Temps
* #ORM\OneToOne(targetEntity="Temps")
*/
private $temps;
/**
* #var Employer
* #ORM\OneToOne(targetEntity="Employer")
*/
private $employer;
but when i do all that I have this error message :
Cannot use object of type Symfony\Component\HttpFoundation\ParameterBag as array
Symfony 2.8 ParameterBag
you are accessing the request parameters like arrays use
mixed get(string $key, mixed $default = null, bool $deep = false)
$historique->setPoste($request->request->get('poste_id'));
change the rest and you are good go.
I've got the following classes (only show partials to reduce amount to read)
class Page {
/**
* #ORM\Column(type="string", unique=true, nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
* #var string
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Url", mappedBy="content")
* #var Url[]
*/
protected $urls;
public function __construct()
{
$this->urls = new ArrayCollection();
}
}
And:
class Url
{
/**
* #ORM\Id #ORM\Column(type="string", unique=true, nullable=false)
* #ORM\GeneratedValue(strategy="UUID")
* #var string The unique identifier for the Url
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Page", inversedBy="urls", cascade={"persist", "merge"})
* #ORM\JoinColumn(name="content_id", referencedColumnName="id")
* #var int The UUID of the content
*/
protected $content;
public function __construct(Page $content, $link)
{
$this->content = $content;
$this->content->addUrl($this);
}
}
Each of these has a manager class with a save() function which just uses persist() and flush(). Saving them is then done as:
$pageManager->save($post);
$url = new Url($post, 'link goes here');
$urlManager->save($url);
I've also tried:
$url = new Url($post, 'link goes here');
$pageManager->save($post);
$urlManager->save($url);
Though in both instances I get:
( ! ) Fatal error: Uncaught exception 'Doctrine\ORM\ORMInvalidArgumentException' with message 'A managed+dirty entity Page#000000003d5a4ca10000000133ba3c3e can not be scheduled for insertion.'
I've tried this both with and without using AnnotationReader being used with EntityManager::create()
Doctrine's schema validator doesn't report any errors either:
php vendor/bin/doctrine orm:validate-schema
[Mapping] OK - The mapping files are correct.
[Database] OK - The database schema is in sync with the mapping files.
Any ideas how to get the persist() to work?
figured it out:
I had to reverse the order they were saved in, so the Url is saved first, and then Page (though in doing so it's opened up another issue I need to resolve where it wants to persist the author entity but can't (it thinks it's new, but it's not). Thought maybe it was detached, but even a merge() doesn't solve it.
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
* #var string The UUID of the author for the {#link Page}
*/
protected $author;
You must persist the $post and the $url objects before flush(). Otherwise you're going to have this error message.
Try to do this...
$entityManager->persist($post);
$url = new Url($post, 'link goes here');
$entityManager->persist($url);
$entityManager->flush();
Or you can create a flag param on your managers, to not flush if false...
$flush = false;
$pageManager->save($post, $flush);
$url = new Url($post, 'link goes here');
$urlManager->save($url);
It will probably work without any additional resources.
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;
I have run into a problem that defies all my attempts to unproblem it. Its probably simple, I have just totally exhausted myself on this one :)
Basically I want to give the user the ability to add modules (facebook id, a bio, text inputs) and assets (image logo, pdf, et al) to a page object.
I have set up a onetomany relationship for Module and Asset to Page.
Module works as expected, however Asset will not work at all: when PageController calls getAsset() from its entity, it is null. There is no error until I try to iterate over the Assets.
Also, in PageController there are the following namespace declarations:
use Linkme\SiteBundle\Entity\Module;
use Linkme\SiteBundle\Entity\Asset;
If I remove the Module declaration I get an error, but if I remove the Asset line, nothing changes. Therefore I believe the relationship is not created.
If I run
app/console doctrine:schema:create --dump-sql
then amongst others I get the following line:
ALTER TABLE Asset ADD CONSTRAINT FK_C36E75589D3B65E3 FOREIGN KEY (pageId) REFERENCES Page(id);
which makes me think the schema is correct. It as least understands Asset is related to Page
Im starting to feel I have a typo or I am totally missing something equally as obvious - any assistance on troubleshooting or other suggestions would be much appreciated!
app/console --version
Symfony version 2.0.1 - app/dev/debug
Page.php
/*
* #ORM\OneToMany(targetEntity="Asset", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $assets
*/
protected $assets;
/**
* #ORM\OneToMany(targetEntity="Module", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $modules
*/
protected $modules;
/**
* Set assets
* #param Asset $assets
*/
public function setAssets(Asset $assets = null)
{
$this->assets = $assets;
}
/**
* Get assets
*
* #return Asset
*/
public function getAssets()
{
echo '<br />Asset is '.gettype($this->assets); // outut: Asset is NULL
return $this->assets;
}
/**
* Set modules
* #param Module $modules
*/
public function setModules(Module $modules = null)
{
$this->modules = $modules;
}
/**
* Get modules
* #return Module
*/
public function getModules()
{
echo '<br />Module is '.gettype($this->assets); // output: Module is object
return $this->modules;
}
Asset.php
/**
* #var integer $pageId
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="assets")
* #ORM\JoinColumn(name="pageId", referencedColumnName="id")
*/
protected $pageId;
Module.php
/**
* #var integer $pageId
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="modules")
* #ORM\JoinColumn(name="pageId", referencedColumnName="id")
*/
protected $pageId;
PageController.php
use Linkme\SiteBundle\Entity\Module;
use Linkme\SiteBundle\Entity\Asset;
/**
* Add modules and assets to a page
*
* #Route("/{id}/wizardmodule", name="page_wizardmodule")
* #Template()
*/
public function wizardmoduleAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$page = $em->getRepository('LinkmeSiteBundle:Page')->find($id);
$modules = $page->getModules();
$assets = $page->getAssets();
depmod
[symfony]
git=http://github.com/symfony/symfony.git
version=v2.0.1
[twig]
git=http://github.com/fabpot/Twig.git
version=v1.1.2
[monolog]
git=http://github.com/Seldaek/monolog.git
version=1.0.1
[doctrine-common]
git=http://github.com/doctrine/common.git
version=2.1.1
[doctrine-dbal]
git=http://github.com/doctrine/dbal.git
version=2.1.1
[doctrine]
git=http://github.com/doctrine/doctrine2.git
version=2.1.1
[swiftmailer]
git=http://github.com/swiftmailer/swiftmailer.git
version=v4.1.1
[assetic]
git=http://github.com/kriswallsmith/assetic.git
version=v1.0.1
[twig-extensions]
git=http://github.com/fabpot/Twig-extensions.git
[metadata]
git=http://github.com/schmittjoh/metadata.git
version=1.0.0
[SensioFrameworkExtraBundle]
git=http://github.com/sensio/SensioFrameworkExtraBundle.git
target=/bundles/Sensio/Bundle/FrameworkExtraBundle
version=v2.0.1
[JMSSecurityExtraBundle]
git=http://github.com/schmittjoh/JMSSecurityExtraBundle.git
target=/bundles/JMS/SecurityExtraBundle
version=v2.0.1
[SensioDistributionBundle]
git=http://github.com/sensio/SensioDistributionBundle.git
target=/bundles/Sensio/Bundle/DistributionBundle
version=v2.0.1
[SensioGeneratorBundle]
git=http://github.com/sensio/SensioGeneratorBundle.git
target=/bundles/Sensio/Bundle/GeneratorBundle
version=v2.0.1
[AsseticBundle]
git=http://github.com/symfony/AsseticBundle.git
target=/bundles/Symfony/Bundle/AsseticBundle
version=v1.0.0
I got it! and as I predicted, it was an incredibly annoying PEBKAC....
The relationship was not being created because the annotations were not being read, because I was missing a * on the annotations comment box..... ddddoooohhhhh!
Page.php
Before:
/* <========================== there is only one * here. It needs to be two: **
* #ORM\OneToMany(targetEntity="Asset", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $assets
*/
protected $assets;
After:
/** <========================== like this.
* #ORM\OneToMany(targetEntity="Asset", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $assets
*/
protected $assets;
I'd just like to say a big thanks to everyone who helped out with this problem.
Check this:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional
Do you correctly initialize collections into your page constructor?
This is because you don't initialize your $assets collection in your Page constructor.
public function __construct(){
$this->assets = new ArrayCollection();
}
Then I think you haven't run the doctrine:generate:entities command, it simplifies your life a little, creating get, set and add methods for every mapped field. In your case it would create a
public function addModules(Module $modules)
{
$this->modules[] = $modules;
}
Note that actually you simply assign $modules to $this->modules, this is wrong, it's an array not a scalar.
And to add the reference to the page referred by every module you'll have to add another instruction:
public function addModules(Module $modules)
{
$this->modules[] = $modules;
$modules->setPage($this);
}
I've done this in my code and it works, let me know if it works for you too, bye
Linuxatico