Doctrine ORM query builder fetch key value pair - php

I have the following code in an entity repository:
$rows = $this->createQueryBuilder('t')
->select(['t.idColumn', 't.valueColumn'])
->where('t.foo = :foo')
->orderBy('t.idColumn', 'ASC')
->setParameter('foo', $foo)
->getQuery()
->getArrayResult(); // This returns [[idColumn => ..., valueColumn => ...], ...]
$data = [];
foreach ($rows as $row) {
$data[$row['idColumn']] = abs($row['valueColumn']); // Remapping to [id => value]
}
return $data;
Is there any way to get rid of the custom remapping natively? I know that you can use the indexBy parameter, but that only gets me the correct keys but not values.
P.S. I know of array_column(), but that's an extra step that I have to make every time, not to mention it doesn't work on methods that entities have.
P.P.S. This is not using Symfony.

It does not appear this is a feature implemented in the QueryBuilder, however fetchAllKeyValue was added in DBAL 2.11 to the Connection object.
Commit, usage

Related

How to add a column in Symfony default query builder

I am new in Symfony, and I have a problem with an existing application I maintain.
In one of the repositories, there's is a method, that selecting the failed transactions, and the related payment.
Now, they have asked me, to allow filter the transactions, based on the total amount of failed transactions which could be either 1 failed transaction or 2.
What I am trying to do in the query builder, is something like that:
$this
->createQueryBuilder('t')
->join('t.payment', 'p')
->leftJoin( Transaction::class, 'tr', Query\Exprt\Join::WITH, 'p.id = tr.payment')
->groupBy('tr.id');
Until that point everything is fine. The query is executed normally, and I can see the transactions I need.
Now the problem is that I cannot use the following statement:
$this
->createQueryBuilder('t')
// This is the column I need to insert
->addSelect('COUNT(tr.id) AS TotalRecords')
->join('t.payment', 'p')
->leftJoin( Transaction::class, 'tr', Query\Exprt\Join::WITH, 'p.id = tr.payment')
->groupBy('tr.id');
Because the output looks like that:
array:1 [▼
0 => array:2 [▼
0 => Transaction {#1221 ▶}
"TotalRecords" => "1" <- This is the total transactions number I need
]
]
Instead of the output above, I need to have the TotalRecords inside the Transaction Object.
So, Is there a way to achieve that with the query builder? Do you think I do something wrong?
you can just loop over your result set and set TotalRecords on all Transaction objects... and return an array of Transactions, as you probably have hoped. The overhead is minimal but the standard doctrine hydration isn't smart enough
// the following is your query:
$qb = $this
->createQueryBuilder('t')
->addSelect('COUNT(tr.id) AS TotalRecords')
->join('t.payment', 'p')
->leftJoin( Transaction::class, 'tr', Query\Exprt\Join::WITH, 'p.id = tr.payment')
->groupBy('tr.id');
// fetch the results, and instead of straight returning them, "merge"
$results = $qb->getQuery()->getResult();
$return = [];
foreach($result as $row) {
$row[0]->totalCount = $row['TotalCount'];
$return[] = $row[0];
}
return $return; // <-- now an array of Transaction
you also could just not use addSelect but instead having and just use the number of transactions you want to filter by, as a parameter (unless the filtering is done later, in which case that approach won't work)

How to get a Doctrine 2 result as an associative array?

I want to get the results in array for this code:
$person = $em->find('Person', 2);
I am using doctrine 2. I want the above result in array form. .
PHP version 5.4
The best approach is to write method in your Repository class or create query builder inline (but it is not recommended).
use Doctrine\ORM\Query;
...
$qb = $em->getRepository(Person::class)->createQueryBuilder('p');
$qb
->andWhere('p.id = :id')
->setParameter('id', $id)
;
$person = $qb->getQuery()->getResult(Query::HYDRATE_ARRAY);
Replace
$qb->getQuery()->getResult(Query::HYDRATE_ARRAY)
by
$qb->getQuery()->getOneOrNullResult(Query::HYDRATE_ARRAY)
if you need to get only one element.
I have found a solution :
$person = $em->find('Person', 2);
$personx = json_decode(json_encode((array)$person), true);
echo '<pre>';
print_r($personx);
echo '<pre>';
It is working perfectly for me.
As $person may be object with circular references, it can not be converted to array directly, but you can use serializing, like it described here
Symfony Serialize doctrine entity
Or you can do it manually:
$person_array = ['name' => $person->getName(), 'id' => $person->getId()];

Filter an object in Laravel 4.1

I am very new to Laravel and php and i am facing an issue with a collection. The collection is generated in this way:
$users = $media->campaign->users;
Which return this data:
[{id: 1, name: "name", suspended: 0},{id: 2, name: "name2", suspended: 1}]
How can i filter this object in laravel 4.1 to get only the elements that have 0 as suspended?
Use array_filter(array $array[, callable $callback[, int $flag]]):
array_filter($users, function($value) {
return($value->suspended === 0);
});
Check more in Laravel 4.2 documentation, Taylor wrote there that filtering collections use array_filter function. Also you should can use $users = $users->filter(function($user) {}); method.
Also, thanks to #xAoc, you can use filtering on SQL query:
$users = $media->campaign
->users()
->where("suspended", "=", 0)
->get();
Since you're doing a direct "equals" comparison, Laravel's Collection has a where() method you can use. For example:
$users = $media->campaign->users;
$users->where('suspended', 0);
This is a good option if you already have the Collection. If, however, you have control over generating the Collection, it would be more beneficial to only get the actual data you're looking for. In this case, you can add the where clause to the SQL statement, so you will only retrieve the final records you want. For example:
$users = $media->campaign->users()->where('suspended', '=', 0);
NB: the where() method on the Collection and the where() method on the query builder have different signatures. The query builder lets you pass in an operator to use ('=', '>', '<', etc). The Collection only does a direct equals comparison. This trips up a lot of people.

Symfony query wrong result

I'm using a personal request in symfony witch is:
public function findByReferenceUser($Reference, $User)
{
$qb = $this->createQueryBuilder('a')
->select('a')
->join('a.Reference', 'r')
->where('r.id = :refId')
->setParameter("refId", $Reference->getId())
->join('a.User', 'u')
->andWhere('u.id = :userId')
->setParameter("userId", $User->getId());
return $qb->getQuery()->getOneOrNullResult();
}
But it doesn't work properly.
I'm getting 3 results witch are of type NULL,NULL, Boolean. I'm using this to check it:
$list_article = $RArticle->findByReferenceUser($reference, $panier->getUser());
foreach ($list_article as $key => $article)
echo gettype($article);
My database works, and have the right informations.
Finnaly, this works:
$list_article = $RArticle->findAll();
foreach ($list_article as $key => $article)
echo gettype($article);
And print object, object.
So there is my questions: Why do I get NULL, NULL, Boolean in the first case and how do I fixe it?
Thank you for your help! :)
When using getOneOrNullResult, Doctrine will Retrieve a single object. If no object is found null will be returned. here.
If you want several object, you should use getResult
findAll() returns array of objects, such as array('key' => 'object'), that's why you are getting correct records, getOneOrNullResult() returns single object (not array, foreach won't work), if you want to retrieve multiple records you need to use getResult()

Eloquent model mass update

Please correct me if I am wrong, but I think there is no such thing as mass update in an Eloquent model.
Is there a way to make a mass update on the DB table without issuing a query for every row?
For example, is there a static method, something like
User::updateWhere(
array('age', '<', '18'),
array(
'under_18' => 1
[, ...]
)
);
(yes, it is a silly example but you get the picture...)
Why isn't there such a feature implemented?
Am I the only one who would be very happy if something like this comes up?
I (the developers), wouldn't like to implement it like:
DB::table('users')->where('age', '<', '18')->update(array('under_18' => 1));
because as the project grows, we may require the programmers to change the table name in the future and they cannot search and replace for the table name!
Is there such a static method to perform this operation? And if there is not, can we extend the Illuminate\Database\Eloquent\Model class to accomplish such a thing?
Perhaps this was not possible a few years ago but in recent versions of Laravel you can definitely do:
User::where('age', '<', 18)->update(['under_18' => 1]);
Worth noting that you need the where method before calling update.
For mass update/insert features, it was requested but Taylor Otwell (Laravel author) suggest that users should use Query Builder instead. https://github.com/laravel/framework/issues/1295
Your models should generally extend Illuminate\Database\Eloquent\Model. Then you access the entity iself, for example if you have this:
<?php
Use Illuminate\Database\Eloquent\Model;
class User extends Model {
// table name defaults to "users" anyway, so this definition is only for
// demonstration on how you can set a custom one
protected $table = 'users';
// ... code omited ...
Update #2
You have to resort to query builder. To cover table naming issue, you could get it dynamically via getTable() method. The only limitation of this is that you need your user class initialized before you can use this function. Your query would be as follows:
$userTable = (new User())->getTable();
DB::table($userTable)->where('age', '<', 18)->update(array('under_18' => 1));
This way your table name is controller in User model (as shown in the example above).
Update #1
Other way to do this (not efficient in your situation) would be:
$users = User::where('age', '<', 18)->get();
foreach ($users as $user) {
$user->field = value;
$user->save();
}
This way the table name is kept in users class and your developers don't have to worry about it.
A litle correction to #metamaker answer:
DB::beginTransaction();
// do all your updates here
foreach ($users as $user) {
$new_value = rand(1,10) // use your own criteria
DB::table('users')
->where('id', '=', $user->id)
->update(['status' => $new_value // update your field(s) here
]);
}
// when done commit
DB::commit();
Now you can have 1 milion different updates in one DB transaction
If you need to update all data without any condition, try below code
Model::query()->update(['column1' => 0, 'column2' => 'New']);
Use database transactions to update multiple entities in a bulk. Transaction will be committed when your update function finished, or rolled back if exception occurred somewhere in between.
https://laravel.com/docs/5.4/database#database-transactions
For example, this is how I regenerate materialized path slugs (https://communities.bmc.com/docs/DOC-9902) for articles in a single bulk update:
public function regenerateDescendantsSlugs(Model $parent, $old_parent_slug)
{
$children = $parent->where('full_slug', 'like', "%/$old_parent_slug/%")->get();
\DB::transaction(function () use ($children, $parent, $old_parent_slug) {
/** #var Model $child */
foreach ($children as $child) {
$new_full_slug = $this->regenerateSlug($parent, $child);
$new_full_title = $this->regenerateTitle($parent, $child);
\DB::table($parent->getTable())
->where('full_slug', '=', $child->full_slug)
->update([
'full_slug' => $new_full_slug,
'full_title' => $new_full_title,
]);
}
});
}
Laravel 6.*
We can update mass data on query as follow :
Appointment::where('request_id' , $appointment_request->id)->where('user_id', Auth::user()->id)->where('status', '!=', 'Canceled')->where('id', '!=', $appointment->id)->update(['status' => 'Canceled', 'canceled_by' => Auth::user()->id]);
Another example of working code of the mass query and mass update in same instruction:
Coordinate::whereIn('id',$someCoordIdsArray)->where('status','<>',Order::$ROUTE_OPTIMIZED)
->update(['status'=>Order::$ROUTE_OPTIMIZED]);
From Laravel 8 you can also use upsert which helped me updated multiple rows at once with each rows having different values.
https://laravel.com/docs/8.x/eloquent#upserts
laravel 5.8 you can accomplish mass update like so:
User::where('id', 24)->update (dataAssociativeArray) ;

Categories