Symfony2. json output, serialize - symfony

a Symfony2 newbie question.
What do I miss in the following code (it returns a "Call to a member function getArrayResult() on a non-object)"
/**
* Lists all Post entities.
*
* #Route("/jsonout", name="Mario_Post_jsonout")
* #Method("GET")
* #Template()
*/
public function jsonoutAction()
{
$response = new Response();
$em = $this->getDoctrine()->getEntityManager();
$query = $this->getDoctrine()
->getRepository('MarioBlogBundle:Post')
->createQueryBuilder('e')
->select('e')
->getQuery();
$results = $query->execute();
$myArray = $results->getResult(Query::HYDRATE_ARRAY); // neither ->getArrayResult();
$response->setContent(json_encode($myArray));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
Do I need to use a 'use Doctrine\ORM.....;' line? which one exactly?
Thanks

From my experience the methods getResult() and getArrayResult() coming with the $query object not with $result object. So we don't want to use both ->execute() and getResult() together. That's the mistake in your code. we can rewrite your code like,
public function jsonoutAction()
{
$response = new Response();
$em = $this->getDoctrine()->getEntityManager();
$query = $this->getDoctrine()
->getRepository('MarioBlogBundle:Post')
->createQueryBuilder('e')
->select('e')
->getQuery();
$results = $query->getArrayResult(); //or getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);
$response->setContent(json_encode($results));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
it will get worked.

Two suggestions:
1) You do not need to execute query, you can just say
$query->getResult() which will return array of objects. If you
want to have results as pure array you can use
$query->getArrayResult() which will return array of found values.
You also do not need to use select('e') if you want to select all
columns.
2) If you want to get all records without any criteria and condition you can use findAll(); which return array of all objects in your entity:
$em = $this->getDoctrine()->getEntityManager();
$results = $em->getRepository('MarioBlogBundle:Post')->findAll();
Now by installing and using this class EntitySerializer you can have array output or just JSON output (which you want). For your example it will be:
$entitySerializer = new Bgy\Doctrine\EntitySerializer($em);
$jsonOutput = $entitySerializer->toJson($results);

Related

return random data form database in random order and with limit

I'm new with symfony and I'm trying to view data from one of my tables with random order and a limit of 4. I tried doing it in the repository but RAND() is not working so I'm trying in the controller.
The error is the following one:
"Warning: array_rand() expects parameter 1 to be array, object given"
And I don't understand why, when in the $response I set the data into an array.
This is my actual code:
/**
* #Route("/ws/random/superviviente", name="ws_random_survi")
*/
public function randomSurvi(Request $request): Response
{
$data = $request->request->all();
$entityManager = $this->getDoctrine()->getManager();
$randomPerks = $entityManager->getRepository(Perks::class)
->getRandomPerks();
$response = new JsonResponse();
$response -> setStatusCode(200);
$response -> setData(array('random perk' => $randomPerks));
$resultRandom = array_rand($response);
return $resultRandom;
}
You are trying to use array_rand on a doctrine array collection.
You could either convert it as array and back to a doctrine array :
use Doctrine\Common\Collections\ArrayCollection;
public function randomSurvi(Request $request): Response
{
$data = $request->request->all();
$entityManager = $this->getDoctrine()->getManager();
$randomPerks = $entityManager->getRepository(Perks::class)
->getRandomPerks();
$resultRandom = new ArrayCollection(array_rand($randomPerks->toArray()));
return new JsonResponse($resultRandom);
}
Otherwise it would work with shuffle :
$randomPerks = $entityManager->getRepository(Perks::class)->getRandomPerks();
$randomPerks = shuffle($randomPerks);
Or get random perks directly through your method in your repository.
See example from #Krzysztof Trzos:
public function getRandomProducts($amount = 7)
{
return $this->getRandomProductsNativeQuery($amount)->getResult();
}
/**
* #param int $amount
* #return ORM\NativeQuery
*/
public function getRandomProductsNativeQuery($amount = 7)
{
# set entity name
$table = $this->getClassMetadata()
->getTableName();
# create rsm object
$rsm = new ORM\Query\ResultSetMapping();
$rsm->addEntityResult($this->getEntityName(), 'p');
$rsm->addFieldResult('p', 'id', 'id');
# make query
return $this->getEntityManager()->createNativeQuery("
SELECT p.id FROM {$table} p ORDER BY RAND() LIMIT 0, {$amount}
", $rsm);
}
You could write your own query to achieve this, so create a new method inside the repository like so:
public function getRandomPerks(int $limit): array
{
$queryBuilder = $this->createQueryBuilder('p');
return $queryBuilder
->setMaxResults($limit)
->orderBy('RAND()')
->getQuery()
->getResult();
}
Then in your controller all you would to do is call the method and pass a limit:
$randomPerks = $entityManager->getRepository(Perks::class)
->getRandomPerks(4);

Code Style: Doctrine2 queries from entity repository?

As I understand the best code style is to put the complex SQL/DQL queries to the entity repositories.
For example there is an entity named "News". It has an own entity repository named "NewsRepository".
In the controller there is this code:
/**
* #Route("/news", name="news")
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$paginator = $this->get('knp_paginator');
$news = $paginator->paginate(
$em->createQuery('SELECT n FROM AppBundle:News n'),
$request->query->getInt('page', 1),
10
);
return $this->render('app/news/list.html.twig', array(
'news' => $news,
));
}
Now I like to add further features (filtering, order by, ..). That because I think the query should be moved to any service or the entity repository. But how and what is the best coding style?
(And does anybody have nice generic ideas how to easily add filtering, order by ... ?)
Sure,
Every request should be in the repository if you want use it again. Not only complexiest.
I'll try to answer your first question and after give you a tip that could help you with your "generic filtering"
If you want to put your request in the repo just make in your NewsRepository :
public function findAllOrderedByDate() {
$qb = $this->createQueryBuilder('n');
$qb->orderBy('creationDate');
return $qb->getQuery()->getResult();
}
And in your controller :
public function indexAction(Request $request)
{
$newsRepo = $this->get('doctrine')->getRepository('AppBundle:News');
$em = $this->getDoctrine()->getManager();
$paginator = $this->get('knp_paginator');
$news = $newsRepo->findAllOrderedByDate();
$pagination = $paginator->paginate(
$news,
$request->query->getInt('page', 1),
10
);
return $this->render('app/news/list.html.twig', array(
'news' => $pagination,
));
}
For the filtering, you have a trick in your repor that consist by returning the qb and not directly results.
As well, you can make a function that add you orderBy with a given parameter (using addOrderBy() or andWhere) and which return the queryBuilder. After all of that you can process.
EDIT :
The solution i read on this thread :
public function findAllOrderedByDate(callable $func = null) {
$qb = $this->createQueryBuilder('n');
$qb->orderBy('creationDate');
if (is_callable($func)) {
return $func($qb);
}
return $qb->getQuery()->getResult();
}
and in your controller :
$func = function (QueryBuilder $qb) use ($paginator, $request) {
return $paginator->paginate($qb, $request->query->getInt('page', 1), 10);
};
$pagination = $em->getRepository('AppBundle:News')->findAllOrderedByDate($func);
Model part must be in model part. Move queries to repo's.
And what do you mean, saying this: "nice generic ideas how to easily add filtering, order by"?
OrderBy
Where - same link

Symfony2 update row from array

I want update row setting new values from array, now Iset one by one aribute:
$em = $this->getDoctrine()->getManager();
$company = $em->getRepository('CatalogWebBundle:ComCompany')
->findOneBy(
array('cmpCode' => $id)
);
$company->setCmpName($_POST['name']);
$company->setCmpCode($_POST['code']);
$em->flush();
Maybe exist solution to set all atributes from array, some like this ?
$company = $_POST;
Consider using Symfony Form and use like this:
<?php
$request = $this->getRequest(); // or inject Request to the action method like myAction(Request $request)
$em = $this->getDoctrine()->getManager();
$company = $em->getRepository('CatalogWebBundle:ComCompany')
->findOneBy(
array('cmpCode' => $id)
);
$form = $this->createForm('formName', $company);
$form->handleRequest($request);
if($form->isValid()) {
// if you want, you can get entity here,
// but passing an entity in 2nd param of createForm
// provides automatic binding data
$data = $form->getData();
$em->flush();
// do some action after submitting form...
return $this->redirect($this->generateUrl('companies'));
}
Read more about creating forms:
http://symfony.com/doc/current/book/forms.html
And about regitering forms as services for futher use named form in createForm('NAME_HERE', $bindObjectHere):
http://symfony.com/doc/current/book/forms.html#defining-your-forms-as-services

Symfony2 - Setting $categoryTitle without having to manually pass it in the controller

I am using a simple search function, however the problem is that I am hardcoding the category title value in the controller by a findByTitle then passing that into the search function.
Category has a OneToMany/ManyToOne relationship with Post entity.
Have tried the following but got the following error: Error: Call to a member function getTitle() on a non-object
$em = $this->getDoctrine()->getManager();
$query = $this->get('search');
$category = $em->getRepository('AcmeDemoBundle:Category')
->findAll();
$categoryTitle = $em->getRepository('AcmeDemoBundle:Category')
->findByTitle($category->getTitle());
$posts = $query->search($categoryTitle);
How can I dynamically set this value so I don't have to manually pass this in?
Controller
/**
* Search action
*
* #return array
*
* #Route("/search", name="job1_search")
* #Template("AcmeDemoBundle:Job1:search.html.twig")
*/
public function searchAction()
{
// Search code: calling from the service Search
$em = $this->getDoctrine()->getManager();
$query = $this->get('search');
$categoryTitle = $em->getRepository('AcmeDemoBundle:Category')
->findByTitle('job1');
$posts = $query->search($categoryTitle);
return array(
'query' => $query,
'posts' => $posts['results'],
);
}
Search service
public function search($categoryTitle)
{
$results = null;
$query = $this->request->query->get('q');
if (!empty($query)) {
$em = $this->doctrine->getManager();
$results = $em->createQueryBuilder()
->from('AcmeDemoBundle:Post', 'post')
->select('post')
->where('post.category = :category')
->setParameter('category', $categoryTitle)
->andWhere('post.title LIKE :search')
->addOrderBy('post.created', 'DESC')
->setParameter('search', "%${query}%")
->getQuery()
->getResult();
}
return array(
'query' => $query,
'results' => $results,
);
}
The findAll() method sends back an array no matter if you have one or multiple entities persisted
$category = $em->getRepository('AcmeDemoBundle:Category')
->findAll();
It means that on the following lines
$categoryTitle = $em->getRepository('AcmeDemoBundle:Category')
->findByTitle($category->getTitle())
You'd have to change $category->getTitle() which is throwing an error because you are calling the method on the array itself not the entities it contains.
It's hard to give more details without knowing exactly what you are trying to do (use one category or multiple, how is it chosen, ect...)
Ok, let's start from the beginning:
$em = $this->getDoctrine()->getManager();
$query = $this->get('search');
// This will give an ArrayCollection of all AcmeDemoBundle:Category that you have
$category = $em->getRepository('AcmeDemoBundle:Category')
->findAll();
// In here $category is an array, so you cannot get it's title
// Also if you would have an actual entity set as $category - the $categorytitle would
// still be a AcmeDemoBundle:Category entity object, not a title string
$categoryTitle = $em->getRepository('AcmeDemoBundle:Category')
->findByTitle($category->getTitle());
$posts = $query->search($categoryTitle);
You have a search query string, so I would go with that:
// This will contain the entity object
$category = $em->getRepository('AcmeDemoBundle:Category')->findByTitle($query);
To get the title out of that:
$categoryTitle = $category->getTitle();

Symfony2 flash messages from Entity Repository

Is there a way to write flash messages from entity repository. I have a Tag and Category entity.
I am adding tags via Category form, where i added a custom input field that receives tags separated with ", ". Every time i submit a form i check if the tags in the input already exist, if not i add them to the database and then i add them to the Category entity.
Here is my tag repository where im trying to write the flashmessage:
namespace Kpr\CentarZdravljaBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Kpr\CentarZdravljaBundle\Entity\Tags;
use Symfony\Component\HttpFoundation\Request;
class TagsRepository extends EntityRepository
{
public function findByTagInput($arg)
{
$request = Request::createFromGlobals();
$args = explode(", ", $arg);
$em = $this->getEntityManager();
$tagArray = array();
$addaedTags = "";
foreach($args as $name){
$tag = $this->findByName($name);
if(!$tag){
$addaedTags .= $name.", ";
$newTag = new Tags();
$newTag->setName($name);
$newTag->setApproved(1);
$em->persist($newTag);
$tagArray[] = $newTag;
}
}
if($addaedTags!="") $request->get('session')->getFlashBag()->add('info', substr($addaedTags,0,strlen($addaedTags)-2));
$qb = $em->createQueryBuilder();
$qb->add('select', 'tag')
->add('from', 'KprCentarZdravljaBundle:Tags tag')
->add('where', $qb->expr()->in('tag.name', $args));
// $qb instanceof QueryBuilder
$query = $qb->getQuery();
// Set additional Query options
$query->useResultCache('my_cache_id');
$results = $query->getResult();
/*
$sql = "SELECT * FROM categories WHERE $whereS";
$stmt = $this->getEntityManager()->getConnection()->prepare($sql);
$results = $stmt->execute();
*/
$totalResults = array_merge($tagArray, $results);
$tags = new ArrayCollection($totalResults);
return $tags;
}
public function findByName($name)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->add('select', 'tag')
->add('from', 'KprCentarZdravljaBundle:Tags tag')
->add('where', 'tag.name = :namearg')
->setParameter('namearg', $name);
// $qb instanceof QueryBuilder
$query = $qb->getQuery();
$result = $query->getResult();
return $result;
}
}
The error i get:
FatalErrorException: Error: Call to a member function getFlashBag()
on a non-object in /home/kprhr/public_html/CZ_Symfony/src/Kpr/CentarZdravljaBundle/Entity/TagsRepository.php line 31
I don't think you should handle the request from the EntityManager ; their purposes are different. The EntityManager manages the communication between the DB and your application, it's not its purpose to handle session management, neither is it to add flashes messages.
This job should go to a dedicated service, which will handle that.
Now, your error comes from the way you get your request. You are actually creating a brand new object, which is not properly instanciated, by using the static method createFromGlobals. Instead, you should handle the flash messages in your controller.
This would translate as something like that:
//Controller
public function mypageAction()
{
// Your logic
if (count($args) > 0) {
$this->getRequest()->get('session')->getFlashBag()->add('info', implode(',', $args));
}
// ...
}

Resources