symfony get scalar result and entity result - doctrine-orm

I'm using symfony 3, and im trying to createNativeQuery to retrieave a list of product(Entity) and the quantity (Integer) here is my function:
public function findStockbyStore(Store $store)
{
$rsm = new ResultSetMapping();
$rsm->addEntityResult(Product::class, 'p');
$rsm->addFieldResult('p', 'product_id', 'id');
$rsm->addScalarResult('quantity', 'quantity');
$query = $this->getEntityManager()->createNativeQuery(
"SELECT `product_id`, `quantity` as 'quantity' FROM `stock`",
$rsm
);
//$query->setParameter(1, $store->getId());
return $query->getResult();
}
But this query only return an array of products, and if i remove the $rsm->addFieldResult('p', 'product_id', 'id'); i get an array with only the quantity.
Now instead of $query->getResult(); i use $query->getArrayResult(); i get an array containing the id and quantity only.
So how can i get both the Entity and the scalar object?

I think you can't. Doctrine is all about object.
Either you are in an object context in which case you would need a quantity attribute (for example) in your model or you are in a non-object context in which case you have the array.

Related

Search in parent relationship with the database driver

Let's say you have this relationship: users x cats. Each user can have many cats (a "one-to-many" relationship):
class Cat extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Both models (users and cats) have a name field.
Let's say we want to get all cats with bob in their names, using Laravel's Scout.
The standard solution is to add this to the Cat.php model:
// Cat.php
use Searchable;
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
return [
'name' => $this->name,
];
}
And we search with Cat::search('bob')->get().
The problem
The above solution works well, but what if we want to search in the relationship's fields?
What if you want to get cats owned by people with bob in their names?
If you add this to the "Cat" model:
// Cat.php
use Searchable;
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
return [
'name' => $this->name,
'users.name' => '', // no need to return `$this->user->name` as the database engine only uses the array keys
];
}
It won't work. You will get this exception when running Cat::search('bob')->get():
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users.name' in 'where clause'
SQL: select `cats`.* from `cats` where (`cats`.`name` like %bob% or `users`.`name` like %bob%)
Clearly, the SQL is missing the users table. But how to add it? Doing a Cat::join(...)->search('bob') will throw an exception, same for Cat::search(...)->join(...).
The question is: How to search in the parent attributes? And by "parent" I mean the "belongsTo" model.
The query method allows for modifing the search query. Use it to inject a join clause:
Cat::search('bob')->query(function ($builder) {
$builder->select('cats.*')->join('users', 'cats.user_id', '=', 'users.id');
})->get();
This generates the proper query:
SELECT `cats`.*
FROM `cats`
INNER JOIN `users` on `cats`.`genre_id` = `users`.`id`
WHERE (`cats`.`name` LIKE '%bob%' or `users`.`name` LIKE '%bob%')
ORDER BY `id` desc
EDIT: Automatically adds the JOIN clause to all searches:
If you want to search with just Cat::search('bob')->get(), without having to write ->join(...) on every call:
// Cat.php
/**
* Overrides the "search" method to inject a `join` to the relationships.
*/
use Searchable {
Searchable::search as parentSearch;
}
/**
* Perform a search against the model's indexed data.
*
* #param string $query
* #param \Closure $callback
* #return \Laravel\Scout\Builder
*/
public static function search($query = '', $callback = null)
{
return static::parentSearch($query, $callback)->query(function ($builder) {
$builder->select('cats.*')->join('users', 'cats.user_id', '=', 'users.id');
});
}

Salesforce Apex: List.add( value || 'default value')?

After querying for Account sObjects, I'm adding all the fields into a List. The problem is that some Account sObjects have certain fields and others do not. While adding the fields to my list, I'd like to be able to add field if it exists OR add a default string.
Let's go through the example.
My query:
List<Account> accountQuery = [SELECT name, (SELECT name, id FROM Contacts) FROM Account];
My map:
Map<String, List<String>> Result = new Map<String, List<String>>();
Iterating through the queryResult and saving the values:
For(Account i : accountQuery){
String[] temp = new list<string>();
temp.add(i.name)
//Now loop through contacts subquery
if(!i.contacts.isEmpty()){
for(contact j : i.Contacts){
temp.add(j.name || 'No Contact Name Associated'); //Doesn't work
temp.add(j.id || 'No Contact Id Associated');
}
}
else{
temp.add('No Contact Name Associated');
temp.add('No Contact Id Associated');
}
};
How can I achieve this temp.add(value || 'default value')?
Thank you!
What is the purpose of these manipulation with these strings? What do you need to do? I really can't to imagine any use case for this code and I guess that your task might be completed in more convenient way.
Anyway the answer to your question is using ternary operator - ?:
tmp.add(j.name != null ? j.name : 'No Contact Name Associated');

how works createQueryBuilder and leftJoin?

I dont get how to make it work.
I have:
a table partner with fields id and name
a table partner_address with two fields: id_partner and id_address
a table address with fields id and external key id_town which references town(id)
a table town with fields id, a name, and postal_code
I want to select all partners that are in towns with specific postal_code
This query works:
SELECT p.nom, v.nom
FROM partner p
JOIN partner_address pa
ON pa.id_partner=p.id
JOIN address a
ON pa.id_address = a.id
JOIN town t
ON a.id_town=t.id
WHERE t.postal_code='13480';
Now I want to "translate" it into Doctrine 2 full syntax, following the documentation.
So I've made a custom repository:
src/Society/Bundle/MyProjectBundle/Repository/PartnerRepository.php
In this repository, I'm trying to create the corresponding function:
<?php
namespace HQF\Bundle\PizzasBundle\Repository;
use Doctrine\ORM\EntityRepository;
class PartenaireRepository extends EntityRepository
{
/**
* Get all active partners from a given postal code.
*/
public function findAllActiveByCp($cp)
{
return $this->createQueryBuilder('p')
->where('p.dateVFin IS NULL')
->andWhere('p.cp=:cp')
->addOrderBy('p.cp', 'DESC')
->setParameter('cp', $cp);
}
}
Nota: the query in the code is not the right one but this code works in another custom repository I've made, so I'm trying to start from this code.
I'm trying something like this but it doesn't work:
public function findAllActiveByCp($cp)
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftJoin('partner_address pa ON pa.id_partner=p.id')
->leftJoin('address a ON pa.id_address = a.id')
->leftJoin('town t ON a.id_ville=t.id')
->where('p.dateVFin IS NULL')
->andWhere('t.cp=:cp')
->addOrderBy('t.cp', 'DESC')
->setParameter('cp', $cp);
}
I get this error:
Warning: Missing argument 2 for Doctrine\ORM\QueryBuilder::leftJoin(),
called in
/blabla/Repository/PartenaireRepository.php
on line 18 and defined in
/blabla/symfony/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php
line 767
You have to join only properties, that the selected entity have.
In first parameter of join() or leftJoin() or xxxJoin() you pass the attribute name related to selected object, and in the second - alias for joined entity.
Try similar to this:
$q = $this->em()->createQueryBuilder();
$q->select(['item', 'itemContact'])
->from('ModuleAdmin\Entity\CustomerEntity', 'item')
->leftJoin('item.contacts', 'itemContact')
->andWhere($q->expr()->like('item.name', ':customerNameStart'));
Of course, the CustomerEntity contains OneToMany relation in field contacts.
Remember, that in select statement you have to select the root entity (in my example CustomerEntity aliased as item).
Edit by Olivier Pons to add how I found out the solution, and to mark this answer as valid, because it put me on the right track, thank you Adam!
In the file PartenaireRepository.php I've used the createQueryBuilder('p') properly. Here's how to make two joins in a row, using createQueryBuilder():
class PartenaireRepository extends EntityRepository
{
/**
* Retrieval of all partners given for a given postal code.
*/
public function findAllActiveByCp($cp)
{
return $this->createQueryBuilder('p')
->leftJoin('p.adresses', 'a')
->leftJoin('a.ville', 'v')
->where('v.cp=:cp')
->setParameter('cp', $cp);
... blabla
}
}
I believe for what you're doing, you will need to provide four arguments to the leftJoin method.
->leftJoin('partner_address', 'pa', 'ON', 'pa.id_partner = p.id')
So your query builder chain should look like this
public function findAllActiveByCp($cp)
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftJoin('partner_address', 'pa', 'ON', 'pa.id_partner = p.id')
->leftJoin('address', 'a', 'ON', 'pa.id_address = a.id')
->leftJoin('town', 't', 'ON', 'a.id_ville = t.id')
->where('p.dateVFin IS NULL')
->andWhere('t.cp=:cp')
->addOrderBy('t.cp', 'DESC')
->setParameter('cp', $cp)
;
}

Is there possibility to load all rows of table to array in JTable of Joomla 2.5?

I use Joomla 2.5 framework.
I have simple table such as:
int id
string itemid
Now I just want to load all 'itemid's to array using JTable. Is here possibility?
Everything about the JTable Class only deals with one row, as soon as multiple rows are involved, you have two options:
1) Use the solution you gave.
2) Override the load() function in your Table class. This can be done as under:
Put the following load() function in your Table class:
function load($key = null)
{
$db = $this->getDBO();
$query = $db->getQuery(true);
$query->select($key);
$query->from($this->getTableName());
$db->setQuery($query);
$row = $db->loadRowList();
if ($db->getErrorNum())
{
$this->setError($db->getErrorMsg());
return false;
}
// Check that we have a result.
if (empty($row))
{
return false;
}
//Return the array
return $row;
}
Now call the load() function with a single argument 'itemid', and get the array you want.
This worked for me, hope it helps you too.
Solution that I decided to use is inherit model class from JModelList:
class modelModelcomponent extends JModelList
{
protected function getListQuery()
{
// Create a new query object.
$db = JFactory::getDBO();
$query = $db->getQuery(true);
// Select some fields
$query->select('itemid');
// From the hello table
$query->from('#__table');
return $query;
}
}

How to get the first member of the related collection in JPQL

I have Product table which has a related table Images with a relation 1:M.
Class Product {
private Integer productId;
private String productName;
....
....
....
private List<Image> productImageList;
....
....
....
}
Class Image{
private Integer imageId;
private String imageName;
}
Class ProductLite{
private Integer productId;
private String productName;
private String imageName;
}
I am trying a JPQL query where I want to query to fetch products and the first image from the productImageList and returning a ProductLite object using the new constructor.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<ProductLite> getAllProductLite() {
Query q = em.createQuery("SELECT NEW com.mycomp.application.entity.ProductLite(p.productId, p.productName, p.productImageList.get(0).getImageName())"
+ " from Product p"
+ " ORDER by p.productName");
List<ProductLite> prods = q.getResultList();
return prods;
}
But for some reason I am not able to get it to work. I get a NoViableException. So I tried moving the logic of getting the first image (getImage() method) to the Product Entity so in the query I could just call the getImage(). Even that does not seem to work.
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [SELECT NEW com.meera.application.entity.ProductLite(distinct p.productId, p.productName, p.getImage()) from Product p, IN(p.productImageList) pil where p.category.categoryCode = :categoryCode ORDER by p.productName ], line 1, column 52: unexpected token [distinct].
Internal Exception: NoViableAltException(23#[452:1: constructorItem returns [Object node] : (n= scalarExpression | n= aggregateExpression );])
Any help is appreciated.
First, you cannot call methods in entity class from your JP QL query. Second, to use the order of entities in list, you need persisted order.
To create column for order to the join table between image and product, you have to add
#OrderColumn-annotation to the productImageList. For example:
#OrderColumn(name = "myimage_order")
//or dont't define name and let it default to productImageList_order
#OneToMany
private List<Image> productImageList;
Then you have to modify query to use that order to choose only first image:
SELECT NEW com.mycomp.application.entity.ProductLite(
p.productId, p.productName, pil.imageName)
FROM Product p JOIN p.productImageList pil
WHERE INDEX(pil) = 0
ORDER by p.productName