laravel eloquent eager loading nested condition - php

I have 2 tables that is using eager loading and then using nested condition in that eager loading:
//migration for lead table
public function up()
{
Schema::create('leads', function(Blueprint $table)
{
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('first_name',255);
$table->string('surname',255);
});
Schema::table('leads', function($table)
{
$table->foreign('create_by')->references('id')->on('employees')->onDelete('cascade');
});
}
//lead for lead detail emails
public function up()
{
Schema::create('lead_detail_emails', function(Blueprint $table)
{
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('lead_id')->unsigned();
$table->string('email',255);
});
Schema::table('lead_detail_emails',function($table)
{
$table->foreign('lead_id')->references('id')->on('leads')->onDelete('cascade');
});
}
//leads model
class LeadsModel extends Eloquent
{
protected $table = 'leads';
public function emails()
{
return $this->hasMany('LeadDetailEmailsModel','lead_id','id');
}
}
//lead detail emails
class LeadDetailEmail extends Eloquent
{
protected $table = 'lead_detail_email';
public function lead()
{
return $this->belongsTo('LeadsModel');
}
}
When I am trying to add nested condition to eager loading, lets say
$qry = LeadsModel::with(
array
(
'emails' => function($qr)
{
$qr->orWhere('email','like','%testname%');
}
));
$res = $qry->get();
dd($res);
It returns all the records in the lead, I have tried joining emails and $qry by using
->join('lead_detail_emails','lead_detail_emails.lead_id','=','leads.id');
but it does not work as well. may I know what is the problem in the code?
update question
how can i get the leads by doing nested condition on the emails?

$qry = LeadsModel::with(array('emails' => function ($q) use ($input) {
$q->where('email','like',"%{$input}%");
}))->whereHas('emails', function ($q) use ($input) {
$q->where('email','like',"%{$input}%");
});
$res = $qry->get();

Related

In Eloquent how would I check if a table row is associated with another?

Before I get into what my issue is, here is my setup. (FYI I am stuck using Laravel 7.4 at the moment so SOS):
Applications Table
public function up()
{
Schema::create('applications', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
Reports Table
public function up()
{
Schema::create('reports', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
ApplicationReports Table (I know the naming convention is off, but this is how I have to do it for code base)
public function up()
{
Schema::create('applicationReports', function (Blueprint $table) {
$table->unsignedInteger('application_id')->nullable(false);
$table->unsignedInteger('report_id')->nullable(false);
});
}
Here is an example of the ApplicationReports table
application_id
report_id
200
2
Then I have a many to many relationship setup between the Applications and Reports tables like so:
Applications Model
public function reports() {
return $this->belongsToMany(Report::class, 'applicationReports');
}
Reports Model
public function applications() {
return $this->belongsToMany(Application::class, 'applicationReports');
}
In the ReportsController I have a method that will pull all the reports that are in the reports table and then return them, that method looks a little bit like the code below (pseudo coded some of it). But what I am trying to do is only add reports that are associated with applications to the list. When I try the code below doing $report->applications->has($report->id) its returning false and I can't for the life of me figure it out.
public function getReports() {
//Pseudo codeish right here, sorry.
$reports = gets all reports->with(['applications'])->orderBy('name')->get();
$reportsList = [];
foreach ($reports as $report) {
if ($report->applications->has($report->id)) {
$reportsList[] = $report;
}
}
return $reportList;
}
If I dd $report->applications the relationship is there and I can see it under #relations -> #attributes, any help would be appreciated!
The has function is very straight forward.
You can query your reports that only contains applications by doing:
$reports = Report::with('applications')->has('applications')->get();
return $reports;
in other way you can also use whereHas this will accepts Query Builder that you can pass through.
For example:
$reports = Report::with('applications')
->whereHas('applications', function(Builder $query) {
$query->orderBy('created_at');
})->get();
return $reports;

How can I best optmize these queries in Laravel?

I have a simple script in post.blade.php
{{ Auth::user() ? ($post->ratings()->where('user_id', Auth::user()->id)->exists() ? 'You rated ' . $post->userRating($post) : '') : '' }}
The 2 functions that get access in my post model are:
public function ratings() {
return $this->hasMany(Rating::class);
}
public function userRating(Post $post) {
return $this->ratings()->where([
['post_id', '=', $post->id],
['user_id', '=', Auth::user()->id]
])->first('stars')->stars / 2;
}
This is the index in my postcontroller:
public function index() {
$posts = Post::with('user')->withAvg('ratings', 'stars')->paginate(100);
return view('posts.index', [
'posts' => $posts,
]);
}
This however takes about 1 second to load each page because it has to execute that code 100 times.
If I remove the script mentioned at the top from the blade file the page loads way faster.
I made this query in raw MySQL and it loads the results significantly faster:
select `posts`.*, (select avg(`ratings`.`stars`) from `ratings` where `posts`.`id` = `ratings`.`post_id`) as `ratings_avg_stars`, (SELECT count(*) FROM ratings WHERE post_id = posts.id and user_id = 1) as rated from `posts` where `posts`.`deleted_at` is null
If I were to put that in my postcontroller I think the page would load faster, I don't know how to convert the MySQL to Eloquent, I have tried a query converter but those get stuck on the subqueries.
How can I convert the MySQL query to an Eloquent query?
I believe you are looking to Constrain Your Eager Loading and create an access mutator for Post.
You can see my fiddle here.
What this approach does, is load all the necessary data in one go, using the constrained eager load, and the access mutator will have this relationship loaded already when it appends the ratingAverage to the post.
This way you can avoid any unnecessary database calls, or method calls to recompute values you already have.
Schema:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->string('title');
$table->string('body');
$table->timestamps();
});
Schema::create('ratings', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('post_id');
$table->integer('stars');
$table->timestamps();
});
Models:
use \Illuminate\Database\Eloquent\Relations\Pivot;
class User extends Model
{
protected $guarded = [];
}
class Rating extends Pivot
{
protected $table = 'ratings';
protected $guarded = [];
public static function rate(User $user, Post $post, int $stars)
{
return static::create([
'user_id' => $user->id,
'post_id' => $post->id,
'stars' => $stars
]);
}
public function user()
{
return $this->belongsTo(User::class);
}
public function post()
{
return $this->belongsTo(Post::class);
}
}
class Post extends Model
{
protected $guarded = [];
public function user()
{
return $this->belongsTo(User::class);
}
public function ratings()
{
return $this->hasMany(Rating::class);
}
public function getRatingAverageAttribute()
{
return $this->ratings->sum('stars') / $this->ratings->count();
}
}
Controller:
public function index() {
$posts = Post::query()->with(['ratings' => function ($query) {
$query->where('user_id', Auth::id());
}])->get();
return view('posts.index', [
'posts' => $posts,
]);
}
And finally in blade:
#if($post->ratings->count())
{{ 'You rated ' . $post->ratingAverage }}
#endif

How to store data in one to many relations in laravel?

I want to enter some data from another table and save it in a table. I try to use relationship one to many, but I'm confused to save data
This is my database
public function up()
{
Schema::create('jenis_analisas', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('jenis_analisa', 100);
$table->unsignedBigInteger('kategori_id')->nullable();
$table->integer('harga');
$table->timestamps();
$table->foreign('kategori_id')->references('id')->on('kategoris');
});
}
public function up()
{
Schema::create('permintaan_analisas', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('pelanggan_id');
$table->string('judul_penelitian', 100);
$table->string('jenis_contoh', 50);
$table->string('jumlah_contoh');
$table->string('asal_contoh', 200);
$table->unsignedBigInteger('jenisanalisa_id')->nullable();
$table->timestamps();
$table->foreign('pelanggan_id')->references('id')->on('pelanggans');
$table->foreign('jenisanalisa_id')->references('id')->on('jenis_analisas');
});
}
This is my model
class JenisAnalisa extends Model
{
public function kategori()
{
return $this->belongsTo('App\Kategori');
}
public function permintaananalisa()
{
return $this->hasMany('App\PermintaanAnalisa');
}
}
class PermintaanAnalisa extends Model
{
public function jenisanalisa()
{
return $this->belongsTo('App\JenisAnalisa');
}
}
I want to insert multiple data from jenis_analisas table into permintaan_analisas table.

set where for foriegn table in laravel relation

I have a table has many relation to other tables but it seperated with entity value please look at this :
i have this schema
public function up()
{
Schema::create('cards', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('user_id')->unsigned()->nullable();
$table->integer('entity_id');
$table->string('entity');
$table->integer('qty')->nullable()->default('1');
});
}
public function up()
{
Schema::create('tickets', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('title');
$table->string('summary');
$table->integer('amount');
$table->integer('stock')->default('0');
$table->integer('discount')->default('0');
});
}
public function up()
{
Schema::create('products', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('title');
$table->integer('amount');
$table->integer('discount');
$table->text('description');
$table->integer('stock')->default('0');
});
}
and this relation in models
class Card extends Model
{
protected $table = 'cards';
public $timestamps = true;
public function ticket()
{
return $this->belongsTo('App\Models\Ticket', 'entity_id');
}
public function product()
{
return $this->belongsTo('App\Models\Product', 'entity_id');
}
}
i need to set where entity = 'ticket' before use belongsTo i mean is a table hase relation to many table base entity_id and i seperated it by entity column and base same vlue most have realation just.
You can do simply in your eloquent model file. do like this :
public function ticketWithCondition()
{
return $this->belongsTo('App\Models\Ticket', 'entity_id')->where('entity' , 'ticket');
}
public function ticket()
{
return $this->belongsTo('App\Models\Ticket', 'entity_id');
}
call like this :
// for show Card with EntityCondition
$comments = Card::find(123)->with('ticketWithCondition');
// for show comments without EntityCondition
$comments = Card::find(123)->with('ticket');

How to get all items with a certain property in pivot table with eloquent?

I have a 'favourite' functionality for my loops table. I am trying to achieve this with a pivot table. But now I'm trying to find the most efficient way to call all the logged in users favourited loops with eloquent.
loops table :
Schema::create('loops', function(Blueprint $table) {
$table->increments('id');
$table->string('name', 35);
$table->string('loop_path', 255);
$table->string('FK_user_id');
});
users table:
Schema::create('users', function(Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('password', 60);
});
favourites table :
Schema::create('favourites', function(Blueprint $table) {
$table->increments('id');
$table->integer('FK_user_id')->unsigned();
$table->integer('FK_loop_id')->unsigned();
});
Loop.php :
class Loop extends Model {
protected $table = 'loops';
public $timestamps = true;
public function user()
{
return $this->belongsTo('App\User','FK_user_id','id');
}
public function favourites()
{
return $this->belongsToMany('App\User', 'favourites', 'FK_loop_id', 'FK_user_id');
}
}
This is how I achieve this now , but it doesn't seem efficient :
$loops = Loop::with('favourites')->
with('user')->get();
$favouritedLoops = array();
foreach($loops as $loop)
{
//check if logged in user has favourited this
$user_favorites = Favourite::where('FK_user_id', '=', Auth::user()->id)
->where('FK_loop_id', '=', $loop->id)
->first();
if ($user_favorites != null)
{
array_push($favouritedLoops, $loop);
}
}
return Response::json($favouritedLoops);
You should define favouritedLoops method in User model, then You can easily access all favourited loops.
User.php
public function favouritedLoops()
{
return $this->belongsToMany('App\Loop', 'favourites', 'FK_user_id', 'FK_loop_id');
}
and return now will look like:
return Response::json(Auth::user()->favouritedLoops);

Categories