Why soft deleted entities appear in query results? - laravel

I am trying to implement soft deleting concept.
Here is my object:
class Post extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'posts';
protected $softDelete = true;
...
Soft delete is on.
Now, if I 'delete' a post, it gets a 'deleted_at' timestamp:
The problem is, when I search or just use all() to display the posts, the soft deleted items appears there. What is wrong?

Sometimes, you will get the soft deleted table entries with get() even with eloquent and protected $softDelete = true;.
So to avoid this problem, use
...->whereNull('deleted_at')->get();
For example, this query will fetch all rows including soft deleted.
DB::table('pages')->select('id','title', 'slug')
->where('is_navigation','=','yes')
->where('parent_id','=',$parent_id)
->orderBy('page_order')
->get();
So the proper method is,
DB::table('pages')->select('id','title', 'slug')
->where('is_navigation','=','yes')
->where('parent_id','=',$parent_id)
->whereNull('deleted_at')
->orderBy('page_order')
->get();

The soft deleting feature works when using Eloquent. If you are querying the results with query builder you will eventually see all the records trashed and not trashed.
It is not clear in the current docs of Laravel 4, but seeing that the concept of soft deleting just appears under Eloquent ORM - Soft Deleting and not under Query Builder, we can only assume that: soft delete only works with Eloquent ORM.

There is a little trick using soft delete tables and queries in laravel:
When we create something like
$objCars = Car::where("color","blue");
The system executes something like that:
SELECT
*
FROM
cars
WHERE
deleted_at IS NULL
AND
"color" = 'blue'
So far, so good. But, when we apply the "orWhere" method, something funny happens
$objCars = Car::where("color","blue")->orWhere("color","red");
The system will execute something like that:
SELECT
*
FROM
cars
WHERE
deleted_at IS NULL
AND
"color" = 'blue'
OR
"color" = 'red'
This new query will return all the car where deleted_at is null and the color is blue OR if the color is red, even if the deleted_at is not null. It is the same behavior of this other query, what show the problem more explicitly:
SELECT
*
FROM
cars
WHERE
(
deleted_at IS NULL
AND
"color" = 'blue'
)
OR
"color" = 'red'
To escape from this problem, you should change the "where" method passing a Closure. Like that:
$objCars = Car::where(
function ( $query ) {
$query->where("color","blue");
$query->orWhere("color","red");
}
);
Then, the system will execute something like that:
SELECT
*
FROM
cars
WHERE
deleted_at IS NULL
AND
(
"color" = 'blue'
OR
"color" = 'red'
)
This last query, searches for all cars where deleted_at is null and where the color can be or red or blue, as we was want it to do.

I had the same problem and nothing here helped me.
My problem was in my construct, I forgot to call the parent constructor:
public function __construct()
{
parent::__construct();
//Rest of my code
}
Hope that help someone!

Laravel 5.2.44 added withoutTrashed() method to SoftDeletingScope. For example you can use something like this:
Post::withoutTrashed()->get();

Tested it in Laravel 5.6, You need to use SoftDeletes Trait in your model
use Illuminate\Database\Eloquent\SoftDeletes;
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Banners extends Model
{
use SoftDeletes;
//no need of this below line
//protected $softDelete = true;
}
and when you query
$banners = Banners::where('status', 1)->get();
it will not return soft deleted data.

me solved join table with eloquent on softdelete - laravel 8
$query = Model_table_1::join('user', 'user.id', '=', 'table_1.user_id')
->join('table_2', 'table_2.id', '=', 'table_1.table2_id')
->join('table_3', 'table_3.id', '=', 'table_1.table3_id')
->join('table_4', 'table_4.id', '=', 'table_3.table4_id')
->select('table_1.id','table_1.name','table_1.created_at',
'user.name','table_2.name2','table_3.name3','table_4.name4')
->get();
use where in datables
$query = Model_table_1::join('user', 'user.id', '=', 'table_1.user_id')
->join('table_2', 'table_2.id', '=', 'table_1.table2_id')
->join('table_3', 'table_3.id', '=', 'table_1.table3_id')
->join('table_4', 'table_4.id', '=', 'table_3.table4_id')
->select('table_1.id','table_1.name','table_1.created_at','user.name','table_2.name2','table_3.name3','table_4.name4')
->where('table_1.user_id', '=', Auth::user()->id)
->get();
use where in view
$query = Model_table_1::join('user', 'user.id', '=', 'table_1.user_id')
->join('table_2', 'table_2.id', '=', 'table_1.table2_id')
->join('table_3', 'table_3.id', '=', 'table_1.table3_id')
->join('table_4', 'table_4.id', '=', 'table_3.table4_id')
->select('table_1.id','table_1.name','table_1.created_at','user.name','table_2.name2','table_3.name3','table_4.name4')
->where('table_1.user_id', '=', Auth::user()->id)
->first();

Related

How to translate the SQL query to Laravel Eloquent query?

I'm trying to make a complex query using Laravel Eloquent. I know how to do it using raw SQL query, but I don't have any idea how to do it using Eloquent.
Here is my SQL query, and it works perfectly:
select *
from students
where exists(select *
from (select student_movements.id AS sm_id, student_movements.direction, student_movements.deleted_at
from student_movements
inner join student_student_movements
on student_movements.id = student_student_movements.student_movement_id
where students.id = student_student_movements.student_id
and student_movements.deleted_at is null
order by student_movements.id desc
limit 1) as last_sm
where last_sm.direction = 1 AND last_sm.date >= 5-5-2022
);
My models have many-to-many relation using student_student_movements table:
Student
public function studentMovements(): BelongsToMany
{
return $this->belongsToMany(
StudentMovement::class,
'student_student_movements',
);
}
StudentMovement
public function students(): BelongsToMany
{
return $this->belongsToMany(
Student::class,
'student_student_movements'
);
}
My goal is to get all Students, who have the last Movement where direction = 1 and the date of the last Movement >= $someDate.
So, my question is: how to translate the SQL query to Eloquent? I saw many similar questions, but they are not helping me.
Thanks for any advice.
Use the whereHas method, then fine tune the sub query inside the closure to your needs.
You can use the whereHas and orWhereHas methods to define
additional query constraints on your has queries.
There is an example like that in laravel documentation
use Illuminate\Database\Eloquent\Builder;
// Retrieve posts with at least one comment containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
// Retrieve posts with at least ten comments containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
}, '>=', 10)->get();
check the documentation here

Join query using eloquent model mapping

I am trying to do this
select notifications.id, reservations.number from
notifications
JOIN reservations
ON notifications.reservation_id = reservations.id
WHERE notifications.status = 1
using eloquent so I have this this
$await = Notification::with('Reservation')->
select('notifications.id', 'reservations.number')
->where('notifications.status', '=', 1)->get();
return Response::json($awaitLists);
In my Notification model
public function Reservation() {
return $this->belongsTO('Reservation');
}
In my Reservation Model
public function notification() {
return $this->hasMany('Notification');
}
So notification belongs to reservation while reservation has a 1 to many relationship
My question is why can't what I have tried works. I keep getting Unknown column 'reservation.number' but i do have column called number in the reservations table. I know they is a way to use eloquent relationship mapper to do this.
This should do it:
$notifications = Notification::where('status','=',1)->get();
foreach($notifications as $notification) {
$id = $notification->id;
$num = $notification->reservation->number;
$await = [$id,$num];
var_dump($await);
}
The error you're seeing is because eager loading relationships doesn't actually perform a join. It uses two separate queries, and then the relationship fields are assigned after the queries are run.
So, when you do Notification::with('Reservation')->get(), it is running two SQL statements, approximately:
Notification::with('Reservation')->get();
// select * from notifications;
// select * from reservations where id in (?, ?, ...);
You can see the actual queries run with a dd(DB::getQueryLog()), if you're interested.
How you move forward depends on what you need to do. If you need to duplicate your existing query exactly, then you'll need to manually perform the joins.
$notifications = Notification::select('notifications.id', 'reservations.number')
->join('reservations', 'notifications.reservation_id', '=', 'reservations.id`)
->where('notifications.status', '=', 1)
->get();
foreach($notifications as $notification) {
print_r($notification->number);
}
Otherwise, you can just use the objects as they are built by Laravel:
$notifications = Notification::with('Reservation')->where('status', '=', 1)->get();
foreach($notifications as $notification) {
print_r($notification->Reservation->number);
}

Laravel Eloquent ORM eager loading. Relation incorrectly returned as null

I have an Eloquent ORM relationship defined as follows:
ProductConfiguration:
public function product()
{
return $this->belongsTo('Excel\Products\Product');
}
public function currency()
{
return $this->belongsTo('Excel\Currencies\Currency');
}
Product
public function productConfigurations()
{
return $this->hasMany('Excel\Products\ProductConfiguration');
}
public function productType()
{
return $this->belongsTo('Excel\Products\ProductType');
}
I expect that if I do the following that I will load all product configurations of a specified product type, with the related products, nested product type details and the product configuration currency
$results = ProductConfiguration::with(
array(
'product' => function($query) use ($product_type) {
$query->where('product_type_id' , $product_type);
},
'product.productType',
'currency'
)
)
->get();
however the returned collection has 'product' set to NULL. the Currency Relationship is there, but the product relationship is not. I can see the outputted SQL queries and the query that selects the products retrieves the correct products if I paste it directly into my sql editor
select * from `products`
where `products`.`id` in ('12', '13')
and `product_type_id` = '1'
Am I correct to think that the results from this query should be included in my collection, or is there some obvious flaw in my thinking?
I think you don't want to achieve that. Now what you get is getting all ProductConfiguration with products that are only of certain_type.
So in case you have some configuration that has other type for product you will get null because you limited results from product to only the one that has certain product type.
I might be wrong, but you probably wanted to get those ProductConfiguration that belongs to Product that is type of certain_type. In this case you should use whereHas:
$results = ProductConfiguration::
with('product', 'product.productType', 'currency')->
whereHas('product', function($q) use ($product_type)
{
$q->where('product_type_id', '=', $product_type);
})->get();
I hate to post this as an answer but since i don't have enough rep to comment so try this first:
$results = ProductConfiguration::with('product')->get();
dd($results->toArray());
See what you get, if you get some data, try this
$results = ProductConfiguartion::with(array('products' => function($query){
$query->where('product_type_id' , $product_type);
})->get();
dd($results);
See what you get, if you get null: your $product_type variable may be something you didnt expect, so try dd($product_type) to make sure its what your expecting.

Laravel eloquent and relationship

I have a code:
$response = $this->posts
->where('author_id', '=', 1)
->with(array('postComments' => function($query) {
$query->where('comment_type', '=', 1);
}))
->orderBy('created_at', 'DESC')
->limit($itemno)
->get();
And when I logged this query with:
$queries = \DB::getQueryLog();
$last_query = end($queries);
\Log::info($last_query);
In log file I see follow:
"select * from `post_comments` where `post_comments`.`post_id` in (?, ?, ?, ?) and `comment_type` <> ?"
Why is the question mark for comment_type in the query?
Update #1:
I replaced current code with following and I get what I want. But I'm not sure it is OK. Maybe exists many better, nicer solution.
$response = $this->posts
->where('author_id', '=', 1)
->join('post_comments', 'post_comments.post_id', '=', 'posts.id')
->where('comment_type', '=', 1)
->orderBy('created_at', 'DESC')
->limit($itemno)
->get();
Behind the scene the PDO is being used and it's the way that PDO does as a prepared query, for example check this:
$title = 'Laravel%';
$author = 'John%';
$sql = "SELECT * FROM books WHERE title like ? AND author like ? ";
$q = $conn->prepare($sql);
$q->execute(array($title,$author));
In the run time during the execution of the query by execute() the ? marks will be replaced with value passed execute(array(...)). Laravel/Eloquent uses PDO and it's normal behavior in PDO (PHP Data Objects). There is another way that used in PDO, which is named parameter/placeholder like :totle is used instead of ?. Read more about it in the given link, it's another topic. Also check this answer.
Update: On the run time the ? marks will be replaced with value you supplied, so ? will be replaced with 1. Also this query is the relational query, the second part after the first query has done loading the ids from the posts table. To see all the query logs, try this instead:
$queries = \DB::getQueryLog();
dd($queries);
You may check the last two queries to debug the queries for the following call:
$response = $this->posts
->where('author_id', '=', 1)
->with(array('postComments' => function($query) {
$query->where('comment_type', '=', 1);
}))
->orderBy('created_at', 'DESC')
->limit($itemno)
->get();
Update after clarification:
You may use something like this if you have setup relation in your Posts model:
// $this->posts->with(...) is similar to Posts::with(...)
// if you are calling it directly without repository class
$this->posts->with(array('comments' =. function($q) {
$q->where('comment_type', 1);
}))
->orderBy('created_at', 'DESC')->limit($itemno)->get();
To make it working you need to declare the relationship in your Posts (Try to use singular name Post if possible) model:
public function comments()
{
return $this->hasmany('Comment');
}
Your Comment model should be like this:
class Comment extends Eloquent {
protected $table = 'post_comments';
}

laravel search many to many Relashionship

I am testing eloquent for the first time and I want to see if it suit my application.
I have Product table:
id, name
and model:
class Produit extends Eloquent {
public function eavs()
{
return $this->belongsToMany('Eav')
->withPivot('value_int', 'value_varchar', 'value_date');
}
}
and eav table:
id, name, code, field_type
and pivot table:
product_id, eav_id, value_int, value_varchar, value_date
class Eav extends Eloquent {
public function produitTypes()
{
return $this->belongsToMany(
'ProduitType'
->withPivot('cs_attributs_produits_types_required');
}
All this is working.
But I want to search in that relashionship:
e.g: all product that have eav_id=3 and value_int=3
I have tested this:
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->get();
But I get all the product, and eav data only for these who have id=3 and value_int=3.
I want to get only the product that match this search...
Thank you
I know the question is very old. But added the answer that works in the latest versions of Laravel.
In Laravel 6.x+ versions you can use whereHas method.
So your query will look like this:
Produit::whereHas('eavs', function (Builder $query) {
// Query the pivot table
$query->where('eav_id', 3);
})->get()
My suggestion and something I like to follow is to start with what you know. In this case, we know the eav_id, so let's go from there.
$produits = Eav::find(3)->produits()->where('value_int', '3')->get();
Eager loading in this case isn't going to save you any performance because we are cutting down the 1+n query problem as described in the documentation because we are starting off with using find(). It's also going to be a lot easier to read and understand.
Using query builder for checking multiple eavs
$produits = DB::table('produits')
->join('eav_produit', 'eav_produit.produit_id', '=', 'produits.id')
->join('eavs', 'eavs.id', '=', 'eav_produit.eav_id')
->where(function($query)
{
$query->where('eav_produit.value_int','=','3');
$query->where('eavs.id', '=', '3');
})
->orWhere(function($query)
{
$query->where('eav_produit.value_int','=','1');
$query->where('eavs.id', '=', '1');
})
->select('produits.*')
->get();
Making it work with what you already have...
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
$query->orWhere('id', '1')->where('value_int', '1');
}))->get();
foreach($produits as $produit)
{
if(!produit->eavs)
continue;
// Do stuff
}
From http://four.laravel.com/docs/eloquent:
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, you wish to pull all blog posts that have at least one comment. To do so, you may use the has method
$posts = Post::has('comments')->get();
Using the "has()" method should give you an array with only products that have EAV that match your criteria.
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->has('eavs')->get();

Resources