Best practice to delete array of objects in Doctrine - php

i have a question. I'm trying to delete array of objects and thinking that the approach i'm trying to implement could be optimized more.
Does this function sends only one query to database to delete all of the objects ?
public function removeData(array $files)
{
foreach ($files as $file) {
$this->entityManager->remove($file);
}
$this->entityManager->flush();
return true;
}
Maybe i should use bulk deletion ?
I need some opinions.
Thanks.

You can do it with query builder like this :
$qb = $em->createQueryBuilder();
$qb->delete('Foods', 'f');
$qb->where("f.name = {$pizza}");

Related

Is it possible to add custom functions on Laravel Models?

I have an Orders table that has relations to a Movements table, and im constantly doing things like this to calculate several common values for each order:
$warehouse = 7;
$order = Order::find(16111);
$entries = Movement::selectRaw("SUM(gross) AS total_gross")
->selectRaw("SUM(net) AS total_net")
->selectRaw("SUM(qty) AS total_qty")
->where('order_id', $order->id)
->where('to_id', $warehouse)
->first();
$exits = Movement::selectRaw("SUM(gross) AS total_gross")
->selectRaw("SUM(net) AS total_net")
->selectRaw("SUM(qty) AS total_qty")
->where('order_id', $order->id)
->where('from_id', $warehouse)
->first();
is it possible to create a custom function to just query the DB doing something like this:
$warehouse = 7;
$entries = Order::find(16111)->entries($warehouse);
$exits = Order::find(16111)->exits($warehouse);
If so how can it be done?
Thanks for your help...
Absolutely. What you are looking for is called local Query Scopes; it allows you to avoid repeating complexe queries in your code.
Local scopes allow you to define common sets of query constraints that you may easily re-use throughout your application.
Write your local query scope in your model and you'll never have to repeat this code again (DRY principle).
Here's an example to give you an idea, you'll need to tweak it to your needs.
In your Order model:
public function scopeEntries($query)
{
$warehouse = $this->warehouse; // Take advantage of Eloquent wherever you can
return $query->movements()->selectRaw("SUM(gross) AS total_gross")
->selectRaw("SUM(net) AS total_net")
->selectRaw("SUM(qty) AS total_qty")
->where('to_id', $warehouse->id);
}
public function scopeExits($query)
{
$warehouse = $this->warehouse; // Take advantage of Eloquent wherever you can
return $query->movements()->selectRaw("SUM(gross) AS total_gross")
->selectRaw("SUM(net) AS total_net")
->selectRaw("SUM(qty) AS total_qty")
->where('from_id', $warehouse->id)
->where('to_id', $warehouse->id);
}
Now in your code, you will be able to simply call $order->entries()->first() to retrieve the first entry but you can also call $order->exits()->get() to retrieve all exits.

Doctrine: use foreach in a find one

I want to use 99% of my code to do two queries.
When accessing api.com/jobs it will display all my jobs, but acessing api.com/jobs/65 will only display that entire job.
My improvement is to have an if before the foreach, to detect whether it is one single id or to list all.
My code looks like this:
$Repo = $entityManager->getRepository('App\Models\Entity\Jobs');
if ($param){
$jobs = $Repo->find($param);
} else{
$jobs = $Repo->findAll();
}
foreach ($jobs as $j_key=>$job){
...
}
The problem is: the findAll() is doing great but I can't enter the foreach with the find() only;
Is any way to do what I want?
If you must use this approach (I would prefer to separate concerns) put the result into an array;
edited
$Repo = $entityManager->getRepository('App\Models\Entity\Jobs');
if ($param){
$jobs[] = $Repo->find($param);
} else{
$jobs = $Repo->findAll();
}
foreach ($jobs as $j_key => $job){
// there is a risk that whats in $jobs might not be what you expect. So check.
if(!$job instanceOf Jobs::class) {
continue;
}
}
alternatively, use findBy as itll return a Collection object which will iterate.
$Repo->findBy(['column' => 'value']);
I would suggest having different pieces of code to deal with the two cases but, if you would rather keep them together:
$Repo = $entityManager->getRepository('App\Models\Entity\Jobs');
if ($param){
$jobs = array($Repo->find($param));
} else{
$jobs = $Repo->findAll();
}
foreach ($jobs as $j_key=>$job){
...
}
This will create an array with a single element in the case a request is made for a single job, and a request for multiple jobs will return all the matching jobs.

Laravel 5.5 Collection where like

i am filtering data using collections. But i need to use like method. I had tried to write like this : ('name', 'LIKE', '%value%') but it did not work.
Here is my method :
protected function filterData(Collection $collection, $transformer) {
foreach (request()->query() as $query => $value) {
$attribute = $transformer::originalAttribute($query);
if (isset($attribute, $value)) {
$collection = $collection->where($attribute, $value);
}
}
return $collection;
}
The 1st question is whether you really know what you are doing. If you take data from database and then filter it just to take some elements it's definitely not the best way because you can take from database for example 100000 records just to finally have only 2 elements and it will kill your application performance.
But assuming you really want to filter using Support collection, there is no where together with LIKE because you are just filtering an array. If you want to use something similar to like instead of:
$collection = $collection->where('name', $value);
you can use:
$collection = $collection->reject(function($element) use ($value) {
return mb_strpos($element->name, $value) === false;
});
depending on what you really have in collection instead of $element->name you might need to use $element['name']

Best / fasted solution to delete a large number of user data in Symfony / Doctrine

I am working on a Symfony 2.8 project that allows registered users to manage contacts, documents, appointments, etc.
Now I would like to implement a feature that allows users to reset/delete all their data. So the user account itself should stay intact but all the data entities should be deleted.
Currently I am using a Doctrine/ORM approach to loop though all entities and delete them:
public function deleteAllData($user) {
$this->currentBatch = 0;
$this->deleteEntities('MyBundle:Contact', $user);
$this->deleteEntities('MyBundle:Document', $user);
...
$this->deleteEntities('MyBundle:...', $user);
$this->flushIfNecessary(true);
}
private function deleteEntities($type, $user) {
$repo = $this->em->getRepository($type);
$entities = $repo->findByUser($user);
foreach ($entities as $entity) {
$repo->delete($entity);
$this->flushIfNecessary();
}
}
private function flushIfNecessary($force = false) {
$this->currentBatch++;
if ($force || $this->currentBatch % 100 == 0) {
$this->em->flush();
$this->em->clear();
}
}
While this works fine, it is very slow. Using different batch sizes I was able to speed the process up a little bit, but it still takes quite long to delete a large number of entries. Of course this is due to all the ORM handling that is done in the background.
It would be much faster to use simple SQL queries:
private function deleteEntities($type, $user) {
$tableName = null;
$userId = $user->getId();
switch ($type) {
case 'MyBundle:Document':
$tableName = 'document';
break;
...
}
if ($tableName && $userId) {
$this->em->getConnection()->query("
DELETE FROM $tableName
WHERE user_id = '$userId'
");
}
}
This solution works as well, but it require to know the internal DB structure, e.g the table names of the different entities, the name of the userId field, etc.
Of course it is not a big deal to get this information but to me this solution seems to be not as clean an the first one.
Is there a way to get the necessary information from Doctrine while still working plain query / bypassing ORM?
Or is there even a better solution to get this done`?
Your first approach is slow because of object hydration. You don't really need to load all records and transform them into objects. You can simply use QueryBuilder:
$qb = $em->createQueryBuilder('c');
$qb->delete('MyBundle:Contact', 'c')
->where('c.user = :user')
->setParameter('user', $user)
->getQuery()
->execute()
;
Or DQL:
$query = $em->createQuery(
'DELETE MyBundle:Contact c
WHERE c.user = :user')
->setParameter('user', $user);
$query->execute();

How to delete multiple row from DB table by laravel?

I want to delete all comments from comments table.I did that by doing this but this is not the laravel way. Anyone answer the correct steps please .
$comments = Comment::where('post_id',$id)->get();
if(count($comments)>1)
{
$comment_id= [];
foreach ($comments as $i)
{
$comment_id[] = $i->id;
}
for($i=0;$i<count($comments);$i++)
{
Comment::find($comment_id[$i])->delete();
}
}
elseif (count($comments)==1)
{
$comments->delete();
}
Since each Eloquent model serves as a query builder, try this, all in one line:
Comment::where('post_id',$id)->delete();
Tested in tinker, works as expected, returns count of deleted rows.
Documentation: https://laravel.com/docs/5.3/queries#deletes
You can try the following approach:
public function deleteAll(Request $request)
{
$ids = $request->ids;
Comment::whereIn('id',explode(",",$ids))->delete();
}
DB::table('users')->whereIn('id', $ids_to_delete)->delete();
or
$org-products()->whereIn('id', $ids)->delete();
Collect only ids from your command like below and destroy by model.
$yourRaws = YourModel::where('name', 'Enver')->get();
...
YourModel::destroy($yourRaws->pluck('id')->toArray());
Enjoy Your Coding !

Categories