I'm performing the following native query in Symfony 2 repository:
$sql = "SELECT f.id, f.title, 'hi' AS custom_field FROM my_feed f";
$em = $this->getEntityManager();
$rsm = new \Doctrine\ORM\Query\ResultSetMapping($em);
$rsm->addEntityResult('MedicalCoreBundle:Feed', 'f');
$rsm->addFieldResult('f', 'id', 'id');
$rsm->addFieldResult('f', 'title', 'title');
$rsm->addFieldResult('f', 'custom_field', 'custom_field');
$query = $em->createNativeQuery($sql, $rsm);
$result = $query->getResult();
I have created the non-mapped property custom_field and its getter and setter getCustomField and setCustomField in the Feed entity.
Mapped fields are hydrated properly but when i add the custom_field RSM i get the following error: Notice: Undefined index: custom_field [...] in symfony\..\AbstractHydrator.php.
Entity code
// non mapped field
private $custom_field;
public function getCustomField()
{
return $this->custom_field;
}
public function setCustomField($number)
{
$this->custom_field = (int) $number;
return $this;
}
What am i doing wrong here ?
You should map custom_field or use addScalarResult instead of addFieldResult
...
$rsm->addScalarResult('custom_field', 'custom_field');
...
result will look like
[
[0=>entity, 'custom_field'=>custom_field value],
[0=>entity, 'custom_field'=>custom_field value],
...
]
Relatvie to your error, you seem to have a F instead of f in custom_Field somewhere.
Related
So far I have tried the following but I keep only getting the main Entity information joined entities do not make it to the result:
Option 1(Using ResultSetMapping Builder):
$rsm = new ResultSetMappingBuilder(
$this->_em,
ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT
);
$rsm->addRootEntityFromClassMetadata(
'CountryApp\StoreBundle\Entity\Product', 'p'
);
$rsm->addJoinedEntityFromClassMetadata(
'CountryApp\StoreBundle\Entity\Category', 'c', 'p', 'category'
);
$rsm->addJoinedEntityFromClassMetadata(
'CountryApp\StoreBundle\Entity\CustomerProductPrice', 'cpp', 'p', 'customerPrices'
);
$result = $this->_em
->createNativeQuery(
'
SELECT
p.id,
p.code,
p.name,
p.cost,
p.rrp,
p.status,
p.notes,
p.out_of_stock_since,
p.available_in,
c.id,
c.name,
c.code,
cpp.id,
cpp.price
FROM product as p
JOIN category as c ON c.id = p.category_id AND p.status != "DELETED"
LEFT JOIN customer_product_price as cpp ON cpp.product_id = p.id AND cpp.customer_id = :customer
', $rsm
)
->setParameter('customer', $customerId)
->getResult(Query::HYDRATE_ARRAY)
;
Option 2:(using QueryBuild and FetchMode)
$qb = $this->createQueryBuilder('p');
$result = $qb
->select('p')
->addSelect('c')
->addSelect('cpp')
->join(
'CountryApp\StoreBundle\Entity\Category',
'c',
Join::WITH,
$qb->expr()
->eq('c', 'p.category')
)
->leftJoin(
'CountryApp\StoreBundle\Entity\CustomerProductPrice',
'cpp',
Join::WITH,
$qb->expr()
->andX(
$qb->expr()
->eq('p', 'cpp.product'),
$qb->expr()
->eq('cpp.customer', ':customer')
)
)
->setParameter('customer', $customerId)
->getQuery()
->setFetchMode(
'CountryApp\StoreBundle\Entity\Category', 'product', ClassMetadata::FETCH_EAGER
)
->setFetchMode(
'CountryApp\StoreBundle\Entity\CustomerProductPrice', 'product', ClassMetadata::FETCH_EAGER
)
->getResult(Query::HYDRATE_ARRAY)
;
Please advise your thoughts as to what could make this work. I want to obtain the following structure:
[
0 => [
Product[
..
]
Category[
..
]
CustomerProductPrice[
..
]
],
1 => [
Product[
..
]
Category[
..
]
CustomerProductPrice[
..
]
],
..
.
]
While using Doctrine you define your relationships inside of your entity.
You can read more here https://symfony.com/doc/current/doctrine/associations.html always read the documentation and best practices. I dunno if you are using Symfony or not, but this is a great example and more understandable than Doctrine docs.
/**
* #ORM\Entity()
*/
class Product
{
// ...
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="products")
*/
private $category;
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
}
As you see here you define an entity that holds all associations and properties.
Normally association will be lazy loaded by default if you call $product->getCategory() you category will be lazy loaded. If you do not like lazy loading you can always eager fetch with
/**
* #ManyToOne(targetEntity="Category", cascade={"all"}, fetch="EAGER")
*/
And you will receive an array of products where each product will have a property named category and will contain Category entity inside it.
This is the main difference between CakePHP because in CakePHP you get all associations separately and in Symfony, you get a tree of associations.
And your queries seems too complex and in most cases, you do not have to modify queries like that at all. But be careful with lazy loads if you lazy load data on huge lists you will end up with poor performance.
I need to fetch all records in database as Array using findAll() in Doctrine, My Query is Something like this
$result = $this->getDoctrine()
->getRepository('CoreBundle:Categories')
->findAll(\Doctrine\ORM\Query::HYDRATE_ARRAY);
even if set Hydration Mode to HYDRATE_ARRAY, am getting results as objects
array:4 [▼
0 => Categories {#323 ▶}
1 => Categories {#326 ▶}
2 => Categories {#329 ▶}
3 => Categories {#332 ▶}
]
what mistake i made?
The findAll() method does not have any parameters. You can, for example, use the repository's createQueryBuilder() method to achieve what you want to do:
use Doctrine\ORM\Query;
// ...
$query = $this->getDoctrine()
->getRepository('CoreBundle:Categories')
->createQueryBuilder('c')
->getQuery();
$result = $query->getResult(Query::HYDRATE_ARRAY);
It's possible to use $query->getArrayResult()
as a shortcut to $query->getResult(Query::HYDRATE_ARRAY)
doctrine hydration modes
The format in which the result of a DQL SELECT query is returned can be influenced by a so-called hydration mode
so you couldn't use it for findAll().you may try this below:
$em = $this->getDoctrine()->getManager();
$result = $em->createQuery('select m from CoreBundle:Categories m')
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
Sorry, I wake up this old topic after years but I find the solution offered by JMS Serializer very elegant to convert arrays of Doctrine objects into associative arrays (e.g. JSON) without having to cutomize/rewrite your repositories findAll() methods.
I think it might help some people here since JMS is a lightweight framework that is really easy to configure.
The code snippet to use will look like this:
$serializer = SerializerBuilder::create()->build();
$employees = getEmployeeRepo->findAll();
$arrEmployees = $serializer->toArray($employees);
I have made this function:
https://gist.github.com/AndreiLN/3708ab829c26cee4711b1df551d1385f
/**
* Converte um objeto Doctrine para um array
* #param $dados
* #param $single define se é uma única execução (Sem recursividade)
* #return array
*/
public function doctrine_to_array($data, $single = false) {
if (is_object($data)) { // Verifica se é array ou objeto
$methods = get_class_methods($data);
$methods = array_filter($methods, function($val){ return preg_match('/^get/', $val); });
$return = [];
if(count($methods)){
foreach($methods as $method){
$prop = lcfirst(preg_replace('/^get/', "", $method));
$val = $data->$method();
if(!$single){
$return[$prop] = $this->doctrine_to_array($val, $single);
} else {
if(!is_array($val) && !is_object($val)){
$return[$prop] = $val;
}
}
}
}
return $return;
} else if(is_array($data)){
if(count($data)){
foreach($data as $idx => $val){
$data[$idx] = $this->doctrine_to_array($val, $single);
}
}
}
return $data; // Retorna o próprio valor se não for objeto
}
If you find some upgrade please let me know.
Explaining more of this function: it's get the doctrine object of array, if it's a object it's reads all get's methods to get all values, if this values is another doctrine object (And single option is not set) it's call the function recursively until it's done.
If the parameter is a array the function will go throught of it and call the method again for all it's values.
It's easy to use, but not tested in all situations.
I'm trying to join two tables and print a value in twig template but I'm having this issue.
This is my Controller action.
/**
* #Route("products/display/{id}")
* #Template()
*/
public function displayAction($id) {
$em = $this->container->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
$qb->select('p', 'pc.catTitle')
->from('EagleShopBundle:Products', 'p')
->leftJoin('EagleShopBundle:ProductCategory', 'pc', \Doctrine\ORM\Query\Expr\Join::WITH, 'pc.id = p.category')
->where($qb->expr()->eq('p.id', '?5'))
->setParameter(5, $id);
$product = $qb->getQuery()->getResult();
return $this->render("EagleShopBundle:global:product.html.twig", array(
'product' => $product,
'image_path' => '/bundles/eagleshop/images/'
));
}
This is my twig file line related to the issue,
<small class="pr_type">{{product.catTitle}}</small>
But instead of printing 'catTitle' I'm having this issue,
Key "catTitle" for array with keys "0" does not exist in
EagleShopBundle:global:product.html.twig
It is simple: getResult() returns an array of objects, even if there is only one. If you are expecting that this query would return only one object use getOneOrNullResult(). But after that and before you are displaying results you need to check if smth is returned (for example: product instanceof Products) or null is returned.
I'm trying to join two tables and print a value in twig template but I'm having this issue.
This is my Controller action.
/**
* #Route("products/display/{id}")
* #Template()
*/
public function displayAction($id) {
$em = $this->container->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
$qb->select('p, pc.catTitle')
->from('EagleShopBundle:Products', 'p')
->leftJoin('EagleShopBundle:ProductCategory', 'pc', \Doctrine\ORM\Query\Expr\Join::WITH, 'pc.id = p.category')
->where($qb->expr()->eq('p.id', '?5'))
->setParameter(5, $id);
$product = $qb->getQuery()->getOneOrNullResult();
return $this->render("EagleShopBundle:global:product.html.twig", array(
'product' => $product,
'image_path' => '/bundles/eagleshop/images/'
));
}
This is my twig file line related to the issue,
<h1>{{product.productTitle}}</h1>
I guess issue is related to this line
$qb->select('p, pc.catTitle')
This is the error I get,
Key "productTitle" for array with keys "0, catTitle" does not exist in
EagleShopBundle:global:product.html.twig
You could try next query:
$qb->select('p, partial pc.{id, catTitle}')
// if you need full productCategory object then write just 'p, pc'
->from('EagleShopBundle:Products', 'p')
->leftJoin('p.category', 'pc')
//productCategory is the field
//in product entity which has relation to product category entity,
//paste your field (not column!) name here
//if it is not productCategory
->where('p.id = :productId')
->setParameter('productId', $id);
P.S.
It is better to move queries to entity repositories :)
P.P.S.
Doctrine partial objects
UPD
Fixed query - with right field name
I'm trying to do the pagination, but there is an error:
[Syntax Error] line 0, col 57: Error: Expected end of string, got 'limit'
I'm not quite sure if this is the right syntax (and logic) to make my query:
public function getFriendsFromTo ($user, $limit, $offset)
{
return $this->getEntityManager()
->createQuery('SELECT f FROM EMMyFriendsBundle:Friend f WHERE f.user='.$user.' limit '.$limit. 'offset' .$offset)
->getResult();
}
Friends and users are related manyToOne and oneToMany, so in the friends table there is a field - user_id.
This is in my controller:
$user = $this->get('security.context')->getToken()->getUser();
$id = $user->getId();
$friends = $user->getFriends();
$result = count($friends)
$FR_PER_PAGE = 7;
$pages = $result/$FR_PER_PAGE;
$em = $this->getDoctrine()->getEntityManager();
$friends = $em->getRepository('EMMyFriendsBundle:Friend')
->getFriendsFromTo($id, $FR_PER_PAGE, $page*$FR_PER_PAGE);
I know that it's stupid and even wrong (especially the third parameter to be $page*$FR_PER_PAGE), but I just wanted to try if the query works, and it didn't.
Nope. Use:
return $this->getEntityManager()
->createQuery('...')
->setMaxResults(5)
->setFirstResult(10)
->getResult();
$towary = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Towar')
->findBy(array(),array(),10,($current-1)*$numItemsPerPage);
You can use findBy 3rd and 4th parameters of method of doctrine repository, which are limit and offset.
Here is the method definition:
findBy(
array $criteria,
array $orderBy = null,
integer|null $limit = null,
integer|null $offset = null
)
Source: http://www.doctrine-project.org/api/orm/2.2/class-Doctrine.ORM.EntityRepository.html
you can also use
$query->getSingleResult();
Doctrine2.6, stumbled upon this old post and tried the DQL way but it did not fit for purpose. So if you want to avoid using DQL because you already have Entities mapped and joined together, you can do paging using matching & Criteria
$criteria = Criteria::create()
->setMaxResults($limit ? $limit : null)
->setFirstResult($offset ? $offset : null)
$result = $em->getRepository('EMMyFriendsBundle:Friend')
->matching($criteria)->toArray();