Error to call a method in Doctrine, Symfony2 - symfony

I don't understand why I get the following error in my Symfony2 project:
Error: Call to a member function getQueryId() on a non-object
Here are my codes:
Bibliorepository:
<?php
namespace Xxxx\XxxxxBundle\Repository;
use Doctrine\ORM\EntityRepository;
/**
* BiblioRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class BiblioRepository extends EntityRepository
{
public function wantAuthor($author)
{
$query = $this->_em->createQuery('SELECT b FROM XxxxBundle:Biblio b WHERE b.author = :author');
$query->setParameter('author', $author);
$result_author = $query->getResult();
return $result_author;
}
}
The getter:
/**
* Get queryId
*
* #return integer
*/
public function getQueryId()
{
return $this->queryId;
}
}
And the controller:
$author = $this->getUser()->getId();
$repository = $this
->getDoctrine()
->getManager()
->getRepository('XxxxBundle:Biblio');
$resultBiblio = $repository->wantAuthor($author);
$resultBiblio->getQueryId();
foreach ($resultBiblio as $id_query) {
$repository = $this
->getDoctrine()
->getManager()
->getRepository('XxXxBundle:Query');
$resultQuery = $repository->wantQuery($id_query);
$titles = $resultQuery->getQuery();
}
return $this->redirect($this->generateUrl("fos_user_profile_show"));
Thank you very much for your help ;)

$resultBiblio is an array with object. You invoke a method on array and this causes error.
You can invoke this method in foreach like
foreach ($resultBilbo as $singleResult){
$singleResult->getQueryId();
}

Var dump results wantAuthor($author) method and make sure you're getting the Author object in return. I bet you're not getting one.

Related

Symfony4: How to recieve data from linked entity?

Orders // orders
Comments // comments for every order
I would like to find latest comment written in this order.
My
Controller:
$orders = $this->getDoctrine()->getRepository(Orders::class)->findAll();
foreach($orders as $order) {
$temp = array(
$order->getId(),
$order->getComments()->findLatest( $order->getId() )
Entity (Comments):
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Orders", inversedBy="comments")
*/
private $orders;
Entity(Order):
/**
* #return Collection|Comment[]
*/
public function getComments(): Collection
{
return $this->comments;
}
Comment Repository:
public function findLatest($value)
{
return $this->createQueryBuilder('c')
->andWhere('c.orders = :val')
->setParameter('val', $value)
->orderBy('c.id', 'DESC')
->setMaxResults(1)
->getQuery()
->getResult()
;
}
But looks like it not working in this way :(
Error:
Attempted to call an undefined method
named "findLatest" of class "Doctrine\ORM\PersistentCollection".
you are trying to call a repository function from another entity
try to change this line :
$order->getComments()->findLatest( $order->getId()
with:
$this->getDoctrine()->getRepository(Comments::class)->findLatest($order->getId);
a better soulution will be that you work with $orders->getComments() array to avoid requesting data from the database inside a loop
You can do this using the class Doctrine\Common\Collections\Criteria.
Entity(Order):
use Doctrine\Common\Collections\Criteria;
...
/**
* Returns the latest comment or false if no comments found under that criteria
*/
public function findLatestComment()
{
$criteria = Criteria::create()
->orderBy(array("id" => Criteria::DESC))
;
return $this->getComments()->matching($criteria)->first();
}
And then you can simply use it like this:
$order->findLatestComment();

symfony no supporting normalizer found while normalizing an entity

I'm working on symfony 4.1.
I defined two normalizer in my service.yml.
api.tone_normalizer:
class: App\Serializer\Normalizer\JnToneNormalizer
tags: [serializer.normalizer]
and
api.wskeytone_normalizer:
class: App\Serializer\Normalizer\ApiWsKeyToneToneNormalizer
tags: [serializer.normalizer]
Here the first normalizer. Is aware about JnTone entities.
<?php
namespace App\Serializer\Normalizer;
use App\Entity\JnTone;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* JnTone normalizer
*/
class JnToneNormalizer implements NormalizerInterface
{
/**
* {#inheritdoc}
*/
public function normalize($object, $format = null, array $context = array())
{
return [
'id' => $object->getId(),
'name' => $object->getName(),
];
}
/**
* {#inheritdoc}
*/
public function supportsNormalization($data, $format = null)
{
return $data instanceof JnTone;
}
}
And the normalizer where I want to call the first one. rootTone is an instance of JnTone entity so I want to call my JnTone normalizer.
<?php
namespace App\Serializer\Normalizer;
use App\Entity\JnWsKey;
use App\Entity\JnTone;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
use Symfony\Component\Serializer\SerializerAwareTrait;
use Symfony\Component\Serializer\SerializerAwareInterface;
/**
* JnWsKey normalizer
*/
class ApiWsKeyNormalizer implements NormalizerInterface, SerializerAwareInterface
{
use NormalizerAwareTrait;
use SerializerAwareTrait;
private $tones;
/**
* {#inheritdoc}
*/
public function normalize($object, $format = null, array $context = array())
{
return [
'id'=>$object->getId(),
'name'=>$object->getName(),
'rootTone'=>$this->serializer->normalize($object->getRootTone(),$format,$context)
];
}
/**
* {#inheritdoc}
*/
public function supportsNormalization($data, $format = null)
{
return $data instanceof JnWsKey ;
}
}
I can't get this working. The first normalizer isn't find
Could not normalize object of type App\Entity\JnTone, no supporting normalizer found.
What I am doing wrong?
I just did not realize that I have to declare all needed normailizer in the serializer definition. I solved it doing:
$encoder = new JsonEncoder();
$serializer = new Serializer(array(
new JnToneNormalizer(),
new JnWsKeyToneNormalizer()
), array($encoder));
Symfony seems to have ObjectNormalizer. I think you may take advantage of it. Check the installation and usage. I think there is also a way to perform serialization of complex nested objects using annotations and groups.

Lifecycle callbacks not working

I'm currently trying out Symfony 4, but I am having some problems with events triggered by database action (prePersist, preUpdate...)
With Symfony 3, I used to use EntityListener to accomplish this, but I found them really convoluted in Symfony 4 documentation. But I also discovered the LifecycleCallbacks, that I used like this:
/**
* #ORM\Entity(repositoryClass="App\Repository\PostRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Post
{
//Attributes and other functions not included for the sake of clarity, but if I use them, consider that they exist
/**
* #ORM\PrePersist
*/
public function setPostSlug()
{
$title = $this->getPostTitle();
$title = strtolower($title);
$keywords = preg_split("/[\s,']+/", $title);
$slug = implode('-', $keywords);
dump($slug);
$this->$slug = $slug;
return $this;
}
}
My post are created through a Symfony form, and before persistence, I want to break down the title I gave to my post in a standardized string that I will use in my URLs to access said post. Unfortunately, the event never trigger on persistence, despite the slug being generated correctly. I tried to do the operation both on prePersist and postPersist events, but none worked. I searched the issue, and saw that LifecycleCallbacks needed a cache clear to be taken into account, but doing so didn't help.
Here is the action responsible for the post creation, if that might help:
/**
* #Route("/admin/create/post", name="admin-create-post")
* #param Request $request
*/
public function createPost(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$post = $form->getData();
$em->persist($post);
$em->flush();
$this->redirectToRoute('main');
}
return $this->render('admin/new_post.html.twig', array(
'form' => $form->createView()
));
}
Would you know the source of the problem, or which other tools I could use to obtain the desired result?
Thanks in advance.
I handle complex Lifecycle with EventListener
for this .. do :
# services.yml
AppBundle\EventListener\YourListener:
tags:
- { name: doctrine.event_listener, event: prePersist }
// YourListener.php
namespace AppBundle\EventListener;
class YourListener {
/**
* #param LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args): void
{
$post = $args->getEntity();
if ($post instanceof Post) {
// Do your job
}
}
}
But I use symfony EventListenerSubscriber Like this:
/**
* This needs to be set through passed argument in case of accident duplicate
*
* #ORM\PrePersist()
*/
public function setTrackingNumber()
{
$this->trackingNumber = NumberCreator::randomStringWithNDigits(self::TRACKING_DIGIT_COUNT);
}
so I think you need do that in your slug setter like this
/**
* #ORM\PrePersist
*/
public function setSlug()
{
$title = $this->getPostTitle();
$title = strtolower($title);
$keywords = preg_split("/[\s,']+/", $title);
$slug = implode('-', $keywords);
dump($slug);
$this->$slug = $slug;
return $this;
}
I think method name is issue ... I hope this is help to you

Symfony2 OneToMany does not use custom repository class

I have to tables
preset_item
id
and
preset_item_element
preset_item_id -> reference to prese_item
element_type
element_id
in PresetItemElement entity there is:
/**
* #var \GGG\ManagerBundle\Entity\PresetItem
*
* #ORM\ManyToOne(targetEntity="GGG\ManagerBundle\Entity\PresetItem", inversedBy="elements")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="preset_item_id", referencedColumnName="id")
* })
*/
private $presetItem;
and in PresetItem
/**
* #ORM\OneToMany(targetEntity="PresetItemElement", mappedBy="presetItem")
*/
private $elements;
public function getElements()
{
return $this->elements;
}
And preset Item have custom Repository class:
/**
* PresetItem
*
* #ORM\Table(name="preset_item")
* #ORM\Entity(repositoryClass="GGG\ManagerBundle\Entity\PresetItemRepository")
*/
class PresetItem
it's looks like that:
<?php
namespace GGG\ManagerBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Doctrine\DBAL\LockMode;
class PresetItemElementRepository extends EntityRepository
{
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) {
return $this->decorate(parent::findBy($criteria, $orderBy, $limit, $offset));
}
public function find($id, $lockMode = LockMode::NONE, $lockVersion = null) {
return $this->decorateElement(parent::find($id, $lockMode, $lockVersion));
}
public function findOneBy(array $criteria, array $orderBy = null) {
return $this->decorateElement(parent::findOneBy($criteria, $orderBy));
}
private function decorateElement($element) {
$object = $this->getEntityManager()
->getRepository(
'GGGManagerBundle:'.$element
->getPresetItemElementType()
->getRepresentationObject()
)->findOneBy(array('id' => $element->getElementId()));
$element->setObject($object);
}
private function decorate($elements) {
foreach($elements as $element) {
$this->decorateElement($element);
}
return $elements;
}
}
So i'm decorating each PresetItemElement with some additional data
and it's work when i;m getting single PresetItemElement object but when i'm getting PresetItem and try iterate getElements()
$entity = $em->getRepository('GGGManagerBundle:PresetItem')->find($id);
foreach($entity->getElements() as $a) {
var_dump($a->getObject());
}
i have null here, it looks like custom PresetItemElementRepository was not executed
What i do wrong?
Doctrine doesn't use repositories internally when loading related entities. Also, when using for example a query or the query builder to retrieve entities, the repository wouldn't be used either.
To do something every time an entity is loaded from the database, you should register an event handler for the postLoad event. See http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html for how to write and register event listeners, and http://docs.doctrine-project.org/en/latest/reference/events.html#lifecycle-events for the available lifecycle events.

Symfony2 and ParamConverter(s)

Accessing my route /message/new i'm going to show a form for sending a new message to one or more customers. Form model has (among others) a collection of Customer entities:
class MyFormModel
{
/**
* #var ArrayCollection
*/
public $customers;
}
I'd like to implement automatic customers selection using customers GET parameters, like this:
message/new?customers=2,55,543
This is working now by simply splitting on , and do a query for getting customers:
public function newAction(Request $request)
{
$formModel = new MyFormModel();
// GET "customers" parameter
$customersIds = explode($request->get('customers'), ',');
// If something was found in "customers" parameter then get entities
if(!empty($customersIds)) :
$repo = $this->getDoctrine()->getRepository('AcmeHelloBundle:Customer');
$found = $repo->findAllByIdsArray($customersIds);
// Assign found Customer entities
$formModel->customers = $found;
endif;
// Go on showing the form
}
How can i do the same using Symfony 2 converters? Like:
public function newAction(Request $request, $selectedCustomers)
{
}
Answer to my self: there is not such thing to make you life easy. I've coded a quick and dirty (and possibly buggy) solution i'd like to share, waiting for a best one.
EDIT WARNING: this is not going to work with two parameter converters with the same class.
Url example
/mesages/new?customers=2543,3321,445
Annotations:
/**
* #Route("/new")
* #Method("GET|POST")
* #ParamConverter("customers",
* class="Doctrine\Common\Collections\ArrayCollection", options={
* "finder" = "getFindAllWithMobileByUserQueryBuilder",
* "entity" = "Acme\HelloBundle\Entity\Customer",
* "field" = "id",
* "delimiter" = ",",
* }
* )
*/
public function newAction(Request $request, ArrayCollection $customers = null)
{
}
Option delimiter is used to split GET parameter while id is used for adding a WHERE id IN... clause. There are both optional.
Option class is only used as a "signature" to tell that converter should support it. entity has to be a FQCN of a Doctrine entity while finder is a repository method to be invoked and should return a query builder (default one provided).
Converter
class ArrayCollectionConverter implements ParamConverterInterface
{
/**
* #var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
function apply(Request $request, ConfigurationInterface $configuration)
{
$name = $configuration->getName();
$options = $this->getOptions($configuration);
// Se request attribute to an empty collection (as default)
$request->attributes->set($name, new ArrayCollection());
// If request parameter is missing or empty then return
if(is_null($val = $request->get($name)) || strlen(trim($val)) === 0)
return;
// If splitted values is an empty array then return
if(!($items = preg_split('/\s*'.$options['delimiter'].'\s*/', $val,
0, PREG_SPLIT_NO_EMPTY))) return;
// Get the repository and logged user
$repo = $this->getEntityManager()->getRepository($options['entity']);
$user = $this->getSecurityContext->getToken()->getUser();
if(!$finder = $options['finder']) :
// Create a new default query builder with WHERE user_id clause
$builder = $repo->createQueryBuilder('e');
$builder->andWhere($builder->expr()->eq("e.user", $user->getId()));
else :
// Call finder method on repository
$builder = $repo->$finder($user);
endif;
// Edit the builder and add WHERE IN $items clause
$alias = $builder->getRootAlias() . "." . $options['field'];
$wherein = $builder->expr()->in($alias, $items);
$result = $builder->andwhere($wherein)->getQuery()->getResult();
// Set request attribute and we're done
$request->attributes->set($name, new ArrayCollection($result));
}
public function supports(ConfigurationInterface $configuration)
{
$class = $configuration->getClass();
// Check if class is ArrayCollection from Doctrine
if('Doctrine\Common\Collections\ArrayCollection' !== $class)
return false;
$options = $this->getOptions($configuration);
$manager = $this->getEntityManager();
// Check if $options['entity'] is actually a Dcontrine one
try
{
$manager->getClassMetadata($options['entity']);
return true;
}
catch(\Doctrine\ORM\Mapping\MappingException $e)
{
return false;
}
}
protected function getOptions(ConfigurationInterface $configuration)
{
return array_replace(
array(
'entity' => null,
'finder' => null,
'field' => 'id',
'delimiter' => ','
),
$configuration->getOptions()
);
}
/**
* #return \Doctrine\ORM\EntityManager
*/
protected function getEntityManager()
{
return $this->container->get('doctrine.orm.default_entity_manager');
}
/**
* #return \Symfony\Component\Security\Core\SecurityContext
*/
protected function getSecurityContext()
{
return $this->container->get('security.context');
}
}
Service definition
arraycollection_converter:
class: Acme\HelloBundle\Request\ArrayCollectionConverter
arguments: ['#service_container']
tags:
- { name: request.param_converter}
It's late, but according to latest documentation about #ParamConverter, you can achieve it follow way:
* #ParamConverter("users", class="AcmeBlogBundle:User", options={
* "repository_method" = "findUsersByIds"
* })
you just need make sure that repository method can handle comma (,) separated values

Resources