Why does this DQL query produce a two dimensional array? - doctrine

$query = $this->getEntityManager()->createQuery('
SELECT COUNT(s) FROM MyDiaryBundle:TrainingSession s
WHERE s.status = :completed
AND s.user = :user')
->setParameter('user',$user)
->setParameter('completed','confirmed');
$result = $query->getResult();
RESULT : array(1) { [0]=> array(1) { [1]=> string(1) "0" } }
And why is the nested array keyed with '1' ?

Use $query->getSingleScalarResult()
To answer your question – as far as I know, getResult() will hydrate an array of entities, so the first dimension holds the list of entities, and the 2nd dimension is the entity as well as any additional fields that are not part of the entity (such as the result of COUNT(s)).
The nested array element is probably keyed with '1' because you haven't provided as alias for the COUNT(s), such as COUNT(s) AS sessionCount.

Related

Eloquent query with a RAW order by

I have a eloquent query to get list items.
This is based on the idea that the sort column would be filled in, but in some circumstances it is not.
If its the case then I would like to order by the name of the list.
More or less what I wrote bellow.
order by CASE WHEN trickysort IS NOT NULL THEN trickysort::text ELSE list_name END
Here is the original query.
$list = Table_Lists::with(["listItems" =>
function($query)
{
$query
->orderBy('trickysort', 'asc')
->with("sublist.listItems");
}
])
->where("list_name","=", $name)
->where("hos_id","=", $hos_id)->get()->first();
return $list ? $list->toArray() : null;
The table structure is as follows.
Parent table -> table_lists
related child table -> table_list_items
the child table items are the one I need sorting. ( trickysort else list_name )
I hope this makes sense.
Im was thinking kind of a RAW query bit in the ->orderBy section of the child table.

linq - parent/child query to select only lowest level

Given a common parent/child table:
Table A
Column Id int
Column Parent_Id int
Column Description text
I would like to only get the nodes that does not have any child nodes.
1,null,"PARENT A"
2,null,"PARENT B",
3,null,"PARENT C",
100,1,"CHILD A1",
101,1,"CHILD A2",
102,2,"CHILD B1"
So for my resultset I would like to only get:
Parent C (as it does not have any child elements), and child A1, B2, B1.
You don't say what exactly you are querying with LINQ, but the general idea is
var leafNodes = nodes.Where(n => nodes.Count(n1 => n1.Parent_Id == n.Id) == 0);
You might wanna prefer Any() method instead of Count() == 0. See Which method performs better: .Any() vs .Count() > 0?
var itemsWithoutChildren = nodes.Where(item=>!nodes.Any(innerItem=>innerItem.Parent_Id==item.Id))

Reversing IQueryable based on passed property for sorting logic

I am implementing sort based on parameter passed to ascending or descending OrderBy method
else if (showGrid.Sortdir == "DESC")
{
alerts = DB.Incidents.OfType<Alert>().Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching)
.OrderByDescending(a => showGrid.Sort);
}
else
{
alerts = DB.Incidents.OfType<Alert>().Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching)
.OrderBy(a => showGrid.Sort);
}
In case of ascending order sorting it works fine but for descending order sorting doesn't work. I debugged the code and I found that list is not revered its same as ascending order. Please help me
Ok. I've written a small test. It is funny, but your code can actually compile and work, but very differently from what you expect :)
Obviously showGrid is not of type Alert, it is an instance of some other class, that incidentally have the same propery as Alert, called Sort.
First I was confused, because expected this code to fail to compile.
// The signature of OrderBy
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
// In your case it will result in
public static IOrderedQueryable<Alert> OrderBy<Alert, string>(this IQueryable<Alert> source, Expression<Func<Alert, string>> keySelector)
//when you call it like you do
DB.Incidents.OfType<Alert>().OrderByDescending(a => showGrid.Sort);
// You supply a property from object of type different from your entity.
// This is incorrect usage, the only object you can use here is the
// "a" argument. Like this:
DB.Incidents.OfType<Alert>().OrderByDescending(a => a.Sort);
// Because anything else does not make any sense to entity provider.
So your order by simply does not work.
As far as I understood, what you want is to perform sorting based on selection in UI. This is not easily achieved in strongly-typed LINQ. Because as I showed above, you send a property, not a value to the OrderBy. It does not care about the value inside the prop. So there are several solutions to the problem:
Write a big switch, that will check every possible Sort value, and will append appropriate 'OrderBy(a => a.YouPropToSort)' to the query. This is straitforward, and you should begin with this. Of course this is a static way, and will require to change code everytime you want new columns to be added for sorting.
Create argument for your OrderBy using 'LINQ Expression Trees'. For you case it should not be very hard to do. Look for the term, you will find a lot of examples.
Try to use Dynamic LINQ. I did not not use it myself, just looked at the docs. This seems to be an extension to the normal LINQ which allows you to write parts of queries as strings, to overcome limitations like the current one with dynamic sorting.
Here's my solution to sorting based on user selections:
Create your base query
var query = DB.Incidents.OfType<Alert>.Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching);
and then apply your sort using a case statement
bool desc = showGrid.SortDir = "DESC";
switch(showGrid.Sort)
{
case "col1":
query = desc ? query.OrderByDescending( a => a.Col1 ) : query.OrderBy( a => a.Col1 );
break;
case "col2":
query = desc ? query.OrderByDescending( a => a.Col2 ) : query.OrderBy( a => a.Col2 );
break;
...
}
var results = query.ToList();

doctrine 2 findby function , get keys and values

can i get the keys and values dynamically.
in php, you would: foreach ($array as $key => $value)
but you can do this for :
$this->_em->getRepository('Members')->findBy(array('id' =>5));
any way to get the keys from this with their values..?
i can do this by turning it into an array and extract it but i wouldnt get any association results inside the array ..
i want to do this as i want to be able to extract all properties and values of this object and extract all other objects within it too..
Ih had the same issue as you now have and after some research i just found a solution which you might be interested.what you need is an associative array of keys/values and not an object.findBy()method only returns entity OBJECT.so you will need to use DQL(doctrine query language).
//create a QueryBuilder instance
$qb = $this->_em->createQueryBuilder();
$qb->add('select', 'a')
//enter the table you want to query
->add('from', 'Members a')
->add('where', 'a.id = :id')
//order by username if you like
//->add('orderBy', 'a.username ASC')
//find a row with id=5
->setParameter('id', '5');
query = $qb->getQuery();
//if you dont put 3 or Query::HYDRATE_ARRAY inside getResult() an object is returned and if you put 3 an array is returned
$accounts = $query->getResult(3);
from doctrine documentation:
13.7.4. Hydration Modes
Each of the Hydration Modes makes assumptions about how the result is
returned to user land. You should know about all the details to make
best use of the different result formats:
The constants for the different hydration modes are:
Query::HYDRATE_OBJECT
Query::HYDRATE_ARRAY
Query::HYDRATE_SCALAR
Query::HYDRATE_SINGLE_SCALAR
To learn more about 'The Query Builder' please refer to doctrine2 documentation
Update:
To fetch associated Entities you will need to define fetch joins.Here is an example provided in doctrine documentation:
$dql = "SELECT b, e, r, p FROM Bug b JOIN b.engineer e ".
"JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC";
$query = $entityManager->createQuery($dql);
$bugs = $query->getArrayResult();
foreach ($bugs AS $bug) {
echo $bug['description'] . " - " . $bug['created']->format('d.m.Y')."\n";
echo " Reported by: ".$bug['reporter']['name']."\n";
echo " Assigned to: ".$bug['engineer']['name']."\n";
foreach($bug['products'] AS $product) {
echo " Platform: ".$product['name']."\n";}
echo "\n";}
The code mentioned above will fetch your entities as array of arrays and you can do whatever you want with $keys and $values.
Hope this helps...

LINQ OrderBy versus ThenBy

Can anyone explain what the difference is between:
tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.OrderBy(sort2 => sort2.InvoiceOwner.FirstName)
.OrderBy(sort3 => sort3.InvoiceID);
and
tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.ThenBy(sort2 => sort2.InvoiceOwner.FirstName)
.ThenBy(sort3 => sort3.InvoiceID);
Which is the correct approach if I wish to order by 3 items of data?
You should definitely use ThenBy rather than multiple OrderBy calls.
I would suggest this:
tmp = invoices.InvoiceCollection
.OrderBy(o => o.InvoiceOwner.LastName)
.ThenBy(o => o.InvoiceOwner.FirstName)
.ThenBy(o => o.InvoiceID);
Note how you can use the same name each time. This is also equivalent to:
tmp = from o in invoices.InvoiceCollection
orderby o.InvoiceOwner.LastName,
o.InvoiceOwner.FirstName,
o.InvoiceID
select o;
If you call OrderBy multiple times, it will effectively reorder the sequence completely three times... so the final call will effectively be the dominant one. You can (in LINQ to Objects) write
foo.OrderBy(x).OrderBy(y).OrderBy(z)
which would be equivalent to
foo.OrderBy(z).ThenBy(y).ThenBy(x)
as the sort order is stable, but you absolutely shouldn't:
It's hard to read
It doesn't perform well (because it reorders the whole sequence)
It may well not work in other providers (e.g. LINQ to SQL)
It's basically not how OrderBy was designed to be used.
The point of OrderBy is to provide the "most important" ordering projection; then use ThenBy (repeatedly) to specify secondary, tertiary etc ordering projections.
Effectively, think of it this way: OrderBy(...).ThenBy(...).ThenBy(...) allows you to build a single composite comparison for any two objects, and then sort the sequence once using that composite comparison. That's almost certainly what you want.
I found this distinction annoying in trying to build queries in a generic manner, so I made a little helper to produce OrderBy/ThenBy in the proper order, for as many sorts as you like.
public class EFSortHelper
{
public static EFSortHelper<TModel> Create<TModel>(IQueryable<T> query)
{
return new EFSortHelper<TModel>(query);
}
}
public class EFSortHelper<TModel> : EFSortHelper
{
protected IQueryable<TModel> unsorted;
protected IOrderedQueryable<TModel> sorted;
public EFSortHelper(IQueryable<TModel> unsorted)
{
this.unsorted = unsorted;
}
public void SortBy<TCol>(Expression<Func<TModel, TCol>> sort, bool isDesc = false)
{
if (sorted == null)
{
sorted = isDesc ? unsorted.OrderByDescending(sort) : unsorted.OrderBy(sort);
unsorted = null;
}
else
{
sorted = isDesc ? sorted.ThenByDescending(sort) : sorted.ThenBy(sort)
}
}
public IOrderedQueryable<TModel> Sorted
{
get
{
return sorted;
}
}
}
There are a lot of ways you might use this depending on your use case, but if you were for example passed a list of sort columns and directions as strings and bools, you could loop over them and use them in a switch like:
var query = db.People.AsNoTracking();
var sortHelper = EFSortHelper.Create(query);
foreach(var sort in sorts)
{
switch(sort.ColumnName)
{
case "Id":
sortHelper.SortBy(p => p.Id, sort.IsDesc);
break;
case "Name":
sortHelper.SortBy(p => p.Name, sort.IsDesc);
break;
// etc
}
}
var sortedQuery = sortHelper.Sorted;
The result in sortedQuery is sorted in the desired order, instead of resorting over and over as the other answer here cautions.
if you want to sort more than one field then go for ThenBy:
like this
list.OrderBy(personLast => person.LastName)
.ThenBy(personFirst => person.FirstName)
Yes, you should never use multiple OrderBy if you are playing with multiple keys.
ThenBy is safer bet since it will perform after OrderBy.

Resources