Bazen SQL sorgusunda ->groupBy('') metodunu çalıştırmak istediğimizde Strict mode hatası alabiliriz. Bu durumda yapmız gereken config/database.php dosyasında strict modu'u kapatmak olur. config/database.php
(l. 54)
'strict' => false,
Post::query()
->with('author')
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return View::make('posts', ['years' => $years]);
Zaman nesnesi zaten belli olduğu için onda Carbon::parse demeden de direk zaman metodlarını çalıştırabiliriz.
{{ $post->published_at->toFormattedDateString() }}
withCount()
metodunu kullanırız.
$feature = Feature::query()->withCount('comments')->paginate();
$most_stikes = Trial::where('premium', true)
->withCount('strikes')
->orderBy('strikes_count','DESC')
->limit(10)
->get();
withCount()
yanı sıra withMin
, withMax
, withAvg
, withSum
, ve withExists
gibi aggregate fonksiyonlar da var.$posts = Post::withSum('comments', 'votes')->get();
$posts = Post::withSum('comments as total_comments', 'votes')->get();
select
count(case when status="Requested" then 1 end) as requested,
count(case when status="Planned" then 1 end) as requested,
count(case when status="Completed" then 1 end) as requested,
from features
$status = Feature::toBase()
->selectRaw("count(case when status = 'Requested' then 1 end) as requested")
->selectRaw("count(case when status = 'Planned' then 1 end) as requested")
->selectRaw("count(case when status = 'Completed' then 1 end) as requested")
->first();
$orderby = $request->has('orderby') ? $request->string('orderby') : 'none';
$filter = $request->has('filter') ? $request->string('filter') : 'none';
$companies = Company::query()
->when($orderby == 'az', function($companies) {
$companies->orderBy('alias');
})
->when($orderby == 'za', function($companies) {
$companies->orderBy('alias','desc');
})
->when($filter == 'premiums', function($companies) {
$companies->where('type_id', 1);
})
->when($filter == 'trials', function($companies) {
$companies->where('type_id', 4);
})
->paginate(100);
{{ $user->logins()->latest()->first()->created_at->diffForHumans() }}
show(Article $article)
metoduna $article geliyor. Ama ben onu almanın ötesinde bir de eager loading eklemek istiyorum. O zaman load()
metodunu kullanabilirim.
public function show(Article $article)
{
$article->load('comments.user');
return view('article-show', ['article' => $article]);
}
$query->join('companies', 'companies.id', '=', 'users.company_id');
public function show(Article $article)
{
$article->load('comments.user');
$article->comments->each->setRelation('article', $article);
return view('article-show', ['article' => $article]);
}
Post::query()
->select('id', 'title', 'slug', 'published_at', 'author_id')
->with(['author' => function($query) {
$query->select('id', 'name')
}])
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return View::make('posts', ['years' => $years]);
hatta with tarafından daha da temiz yazmak istersen ->with('author:id,name')
de diyebilirsin.
Post::query()
->select('id', 'title', 'slug', 'published_at', 'author_id')
->with('author:id,name')
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return View::make('posts', ['years' => $years]);
addSelect()
ile sanki o tabloda varmış gibi subquery üzerinden ekleyebilirsin.
$users = User::query()
->addSelect(['last_login_at' => Login::select('created_at')
->where('user_id', 'users.id')
->latest()
->take(1)
])
->orderBy('name')
->paginate();
Burada yaptığımız şey users tablosuna last_login_at adında sanal bir sütun daha eklemek oluyor. Ama bu metodun çalışması için addSelect'in sadece bir değer dönmesi lazım. O yüzden subquery sonunda ->take(1)
demek zorundayım.
Laravel'de veritabanından bir sorgu dönerken ör. subquery için sıralamayı belli bir özelliğe göre getirmesini istersen subquery yapman gerekir. Şu şekilde:
$product_variation_sets = VariationSet::with(['items' => function($query) {
$query->orderBy('order');
}])
->where('product_id', $product->id)
->get();
Bir tablodaki belli bir set ile başka bir tabloyu anahtar üzerinden birbirine bağlayarak daha büyük bir set alırken burada bir alt koşul talep ediyorum ve bütün bu koşulları olumlu olan setin boyutunu öğrenmek istiyorum
return DB::table('goals')
->where('field_id', $this->id)
->join('tasks', function ($join) {
$join->on('goals.id', '=', 'tasks.goal_id')->where('tasks.is_done', '=', false);
})
->count();
Bu konuda dokumantasyon - Laravel Advanced Join Clauses
Örneğin UsersController'daki metodlardan birinde kullanıcıları son login tarihine göre çektiğimiz bir sorgumuz olsa
$users = User::query()
->addSelect(['last_login_at' => Login::select('created_at')
->where('user_id', 'users.id')
->latest()
->take(1)
])
->withCasts(['last_login_at' => 'datetime'])
->orderBy('name')
->paginate();
Bu sorguda bazı problemler var...
public function scopeWithLastLogin($query)
{
$query->addSelect(['last_login_at' => Login::select('created_at')
->where('user_id', 'users.id')
->latest()
->take(1)
])
->withCasts(['last_login_at' => 'datetime']);
}
User Model'inde bu scope oluştuktan sonra artık bunu User içinde query() with() where() gibi kullanabilirim.
$users = User::query()
->withLastLoginAt()
->orderBy('name')
->paginate();
public function lastLogin()
{
return $this->belongsTo(Login::class);
}
ama bu ilişki aslında One-to-many ve karşılığında last_login_id olmasını bekler. Bu değeri öncelikle dinamik değer olarak hesaplatırsak Laravel bu değerin dinamik getirildiğini algılamadan ilişkiyi doğru biçimde bağlar.
public function lastLogin($query)
{
return $this->belongsTo(Login::class);
}
public function scopeWithLastLogin($query)
{
$query->addSelect(['last_login_at' => Login::select('created_at')
->where('user_id', 'users.id')
->latest()
->take(1)
])->with('lastLogin');
}
public function index()
{
$users = User::query()
->withLastLogin()
->orderBy('name')
->paginate();
return View::make('users', ['users' => $users]);
}
{{ $user->lastLogin->created_at->diffForHumans() }}
{{ $user->lastLogin->ip_address }}
En Yenisi için latestOfMany()
/**
* Get the user's most recent order.
*/
public function latestOrder()
{
return $this->hasOne(Order::class)->latestOfMany();
}
/**
* Get the user's oldest order.
*/
public function oldestOrder()
{
return $this->hasOne(Order::class)->oldestOfMany();
}
/**
* Get the user's largest order.
*/
public function largestOrder()
{
return $this->hasOne(Order::class)->ofMany('price', 'max');
}
/**
* Get the current pricing for the product.
*/
public function currentPricing()
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', now());
});
}
public function index()
{
$users = User::query()
->search(request('search'))
->with('company')
->paginate();
return view('users', ['users' => $users]);
}
public function scopeSearch($query, string $term = null)
{
collect(explode(' ', $term))->filter()->each(function ($term) use ($query) {
$term = '%' . $term . '%';
$query->where(function($query) use ($term) {
$query->where('first_name', 'like', $term)
->orWhere('last_name', 'like', $term)
->orWhereHas('company', function($query) use ($term) {
$query->where('name', 'like', $term);
});
});
});
}
$user->posts()
->where('active', 1)
->orWhere('votes', '>=', 100)
->get();
bunun SQL tarafındaki karşılığı
select *
from posts
where user_id = ? and active = 1 or votes >= 100
yani or'dan sonraki kısımda user_id artık önemsiz hale gelmiştir.
Yani burada X bir kullanıcıya sınırlanan mantık bağı kopmuştur. Sorgu 100+ oy alan bütün postları kullanıcı id'sine bakmaksızın döndürecektir.
Halbuki biz x bir kullanıcının makaleleri arasından aktif olan veya oyu 100+ olanları istiyoruz. Bunun için sorguyu bir mantık grubu olarak belirtmek için parantez içine almamız gerekir.
$user->posts()
->where(function (Builder $query) {
return $query->where('active', 1)
->orWhere('votes', '>=', 100);
})
->get();
Böylece SQL'de yaptığımız sorguyu da
select *
from posts
where user_id = ? and (active = 1 or votes >= 100)
dönüştürmüş olduk.
Dokumantasyondaki açıklaması için link
// Retrieve all posts that have at least one comment...
$posts = Post::has('comments')->get();
dememiz gerekir. Tabii bunun tam tersi de yapılabilir
$posts = Post::doesntHave('comments')->get();
Veya sorguya koşul da ekleyebiliriz
// Retrieve all posts that have three or more comments...
$posts = Post::has('comments', '>=', 3)->get();
Hatta nokta ile iç ilişkiler üzerinden de sorguyu daha net hale getirebiliriz.
// Retrieve posts that have at least one comment with images...
$posts = Post::has('comments.images')->get();
Bütün bunların daha da üstünde bir de iç sorgu yapmak istersek o zaman whereHas veya orWhereHas kullanabiliriz.
// 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();
Tabii bunun tam tersi de mevcut
$posts = Post::whereDoesntHave('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
Dokümantasyon'dan - https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
Schema::create('users', function(Blueprint $table) {
$table->id();
$table->foreignId('company_id')->constrained('companies');
$table->string('first_name')->index();
$table->string('last_name')->index();
$table->timestamp();
});
Schema::create('companies', function(Blueprint $table) {
$table->id();
$table->string('name')->index();
$table->timestamp();
})
'%' . $term . '%'
yerine başında '%' olmadan sorgumuzu çalıştırmamız gerekir.
SELECT * FROM `users`
WHERE (`first_name` LIKE 'bill%' OR `last_name` LIKE 'bill%')
and (`first_name` LIKE 'gates%' OR `last_name` LIKE 'gates%')
and (`first_name` LIKE 'microsoft%' OR `last_name` LIKE 'microsoft%')
LIMIT 15 OFFSET 0
public function scopeSearch($query, string $term = null)
{
collect(str_getcsv($term, ' ', '"'))->filter()->each(function ($term) use ($query) {
$term = $term . '%';
$query->where(function($query) use ($term) {
$query->where('first_name', 'like', $term)
->orWhere('last_name', 'like', $term)
->orWhereIn('company_id', function($query) use ($term) {
$query->select('id')->from('companies')->where('name', 'like', $term);
});
});
});
}
public function scopeSearch($query, string $term = null)
{
collect(str_getcsv($term, ' ', '"'))->filter()->each(function ($term) use ($query) {
$term = $term . '%';
$query->where(function($query) use ($term) {
$query->where('first_name', 'like', $term)
->orWhere('last_name', 'like', $term)
->orWhereIn('company_id', Company::query()->where('name', 'like', $term)->pluck('id'));
});
});
}
Burada hem ana query var hem de her bir terim için ayrıca işleyen bir Company::query() var ama Company::query() zaten inanılmaz optimize olduğu için sonuç yine de çok iyi.