Convert SQL request onto Eloquent - laravel-5

I would like to use Eloquent in a CRUD. This CRUD uses several datas from 4 tables.
admins :
Schema::create('admins', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('surname');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
});
langs :
Schema::create('langs', function (Blueprint $table) {
$table->increments('id');
$table->string('isocode', 10)->nullable();
$table->string('name', 80)->nullable();
$table->timestamps();
});
lang_sector:
Schema::create('lang_sector', function (Blueprint $table) {
$table->integer('lang_id')->index('FK_LANGS');
$table->integer('sector_id')->index('FK_SECTORS');
$table->integer('admin_id')->index('FK_ADMINS');
$table->string('name', 80)->nullable();
$table->string('shortname', 40)->nullable();
$table->text('description', 65535)->nullable();
$table->primary(['lang_id','sector_id']);
});
sectors:
Schema::create('sectors', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
});
And setup relations in the model :
A sector belong to one Admin
An Admin has many Sector
Sector belong to many language
Lang belong to many Sector
in sector model :
public function langs(){
return $this->belongsToMany('App\Lang')->withPivot('name','shortname','description');
}
public function Admin(){
return $this->belongsTo('App\Admin');
}
In lang model :
public function sectors(){
return $this->belongsToMany('App\Sector')->withPivot('name','shortname','description');
}
In Admin model
public function sectors(){
return $this->hasMany('App\Sector');
}
The information I'd like to display can be represented with this SQL request (updated version) :
SELECT DISTINCT
sectors.id,
lang_sector.sector_id,
lang_sector.lang_id,
lang_sector.admin_id,
langs.name,
lang_sector.name,
lang_sector.shortname,
admins.name,
admins.surname,
sectors.created_at,
sectors.updated_at
FROM lang_sector
INNER JOIN
langs ON langs.id = lang_sector.lang_id
INNER JOIN
sectors ON sectors.id = lang_sectors.sector_id
INNER JOIN
admins ON admins.id = lang_sector.admin_id
ORDER BY lang_sector.sector_id;
My questions are :
how to "translate" it in Eloquent and not in RAW SQL ...( $langs = Lang::latest('updated_at')->get();...)
My other issue is that I want to have the logged admin as value...
Thanks for your help !

Thanks to fubar I began to look DB Query builder ...
$sector = DB::table('lang_sector')
->join('langs', 'langs.id', '=', 'lang_sector.lang_id')
->join('sectors', 'sectors.id', '=', 'lang_sector.sector_id')
->join('admins', 'admins.id', '=', 'lang_sector.admin_id')
->select('sectors.id', 'lang_sector.sector_id', 'lang_sector.lang_id', 'lang_sector.admin_id', 'langs.name', 'lang_sector.name', 'lang_sector.shortname', 'admins.name', 'admins.surname', 'sectors.created_at', 'sectors.updated_at')
->distinct()
->orderByRaw('lang_sector.sector_id')
->get();
return view('admin.sectors',compact('sector'));
I check in the view :
{{ dd('sector') }}
As a result I obtain a collection ... Unfortunatelly I have an issue when I want to display the results :
The request is correct but my collection don't display all required information :
Collection {#480 ▼
#items: array:6 [▼
0 => {#380 ▼
+"id": 1
+"sector_id": 1
+"lang_id": 1
+"admin_id": 1
+"name": "Vera"
+"shortname": "TIC"
+"surname": "David"
+"created_at": null
+"updated_at": null
}
1 => {#487 ▶}
2 => {#485 ▶}
3 => {#386 ▶}
4 => {#383 ▶}
5 => {#488 ▶}
]
}
I will rename the different fields of the database in order to make each one unique ...

Related

Why in pivot table result is different as I expected?

In laravel 9 app I create many top many relation with table
return new class extends Migration {
public function up()
{
Schema::create('article_vote', function (Blueprint $table) {
$table->id();
$table->foreignId('article_id')->references('id')->on('articles')->onUpdate('RESTRICT')->onDelete('CASCADE');
$table->foreignId('vote_id')->references('id')->on('votes')->onUpdate('RESTRICT')->onDelete('CASCADE');
$table->boolean('active')->default(false);
$table->date('expired_at')->nullable();
$table->integer('supervisor_id')->nullable()->unsigned();
$table->foreign('supervisor_id')->references('id')->on('users')->onDelete('CASCADE');
$table->mediumText('supervisor_notes')->nullable();
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable();
$table->unique(['vote_id', 'article_id'], 'article_vote_vote_id_article_id_index');
$table->index(['vote_id', 'article_id', 'active', 'expired_at'], 'article_vote_vote_id_article_id_active_expired_at_index');
$table->index([ 'expired_at', 'active',], 'article_vote_expired_at_active_index');
$table->index(['created_at'], 'article_vote_created_at_index');
});
Artisan::call('db:seed', array('--class' => 'articleVotesWithInitData'));
}
In app/Models/Vote.php :
public function articles(): BelongsToMany
{
return $this->belongsToMany(Article::class, 'article_vote', 'vote_id')
->withTimestamps()
->withPivot(['active', 'expired_at', 'supervisor_id', 'supervisor_notes']);
}
and in app/Models/Article.php :
public function votes(): BelongsToMany
{
return $this->belongsToMany(Vote::class, 'article_vote', 'article_id')
->withTimestamps()
->withPivot(['active', 'expired_at', 'supervisor_id', 'supervisor_notes']);
}
Running requests:
$article = Article::getById(2)
->firstOrFail();
$articleVotes = $article->votes;
I got sql :
SELECT `votes`.*, `article_vote`.`article_id` AS `pivot_article_id`, `article_vote`.`vote_id` AS `pivot_vote_id`, `article_vote`.`created_at` AS `pivot_created_at`, `article_vote`.`updated_at` AS `pivot_updated_at`, `article_vote`.`active` AS `pivot_active`, `article_vote`.`expired_at` AS `pivot_expired_at`, `article_vote`.`supervisor_id` AS `pivot_supervisor_id`, `article_vote`.`supervisor_notes` AS `pivot_supervisor_notes`
FROM `votes`
INNER JOIN `article_vote` on `votes`.`id` = `article_vote`.`vote_id`
WHERE `article_vote`.`article_id` = 2
But result is different as I expected, as in article_vote table I have rows : https://prnt.sc/wTE5uaPrQu9v
But I see different with the sql : https://prnt.sc/Os14x5K6unyu
Why 4 rows with different vote id ?
Thanks!
Comparing your two screenshots, it seems like different databases, look at the created_at and updated_at values of the pivot table, they are totally different for all the rows. Could that be a mistake that you're querying, for example, local vs live DBs?

laravel api resources autoincrement

how to laravel API resources add property in Row collection auto increment for row by row
"id" => (int)$this->id,
"fullname" => $this->fullname,
"city" => $city ? $city->name : 'نا مشخص',
"avatar" => ($this->avatar()->first()) ? img($this->avatar()->first()->path, '124x124') : '',
"count" => (int)$this->user_referral_count,
"rate" => autoincrement
if you set a field in your migration as increments it will be automatically incremented.
like bellow:
public function up() {
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
...
...
...
});
}
and after that, when you create a new instance of User model. it's id will increase itself.
create new user like this:
$user = new User;
$user->name = 'hossein';
$user->email= '#hossein#k.t';
...
...
$user->save();

how to retrieve limited number of related model and sort collection by related model in laravel?

I have 3 model
Shop model, Product model, Picture model
I want to retrieve a collection of shops with last 3 Product model with their pictures and sort my collection based on newest product.
I tried leftjoint and joint in laravel 6 to be able to sort the result but i get all shops`product (i only need last 3 product for each shop),
when I use joint I cant retrieve product pictures
I also have tried “with” method in laravel , I couldnt sort the result based on product.creatred_at and also i get all related product in this method too.(as i mentioned i need the last 3 product)
class Shop extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
}
class Product extends Model
{
public function shop()
{
return $this->belongsTo('App\Shop');
}
public function pictures()
{
return $this->morphMany('App\hPicture', 'pictureable');
}
}
Shop::select('shops.*', 'products.id', 'products.shop_id', 'products.name as pname', 'products.user_id', 'products.code', 'products.price')
->with(['pictures', 'products.pictures'])
->leftjoin('products', function ($leftJoin) {
$leftJoin->on('shops.id', '=', 'products.shop_id');
});
$dataList = $dataList->orderBy($field, $order);
$dataList = $dataList->paginate(5)->appends(['sortField' => $field, 'sortOrder' => $order]);
the table layout for product and shop model is:
Schema::create('shops', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug');
$table->string('phone')->nullable();
$table->string('address')->nullable();
$table->timestamps();
$table->string('description')->nullable();
$table->uuid('uuid');
});
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->integer('shop_id')->unsigned();
$table->foreign('shop_id')->references('id')->on('shops');
$table->string('name');
$table->string('code');
$table->string('slug');
$table->integer('price');
$table->uuid('uuid');
$table->timestamps();
});
There are only 2 ways of resolving this:
Either you pull in all products, and trim them in the end(advisable only if not too many products per shop):
$shops = Shop::with(['products' => function($subQuery) {
$subQuery
->with('pictures') //Running in scope of product, also load each product's pictures
->orderBy('created_at', 'desc');
}])
->get();
foreach ($shops as $shop) {
$shop->setRelation('products', $shop->products->take(3));
}
NOTE:
You will load every single product that is linked to the shops you load. You could get memory issues with this.
Take only what you need, but introduce a n+1 query issue(advisable only with small quantities of $shops:
$shops = Shop::get();
foreach ($shops as $shop) {
$shop->load([
'products' => function($query) {
$query
->orderBy('created_at', 'desc')
->limit(3)
->get();
}]);
}
NOTE:
N+1 query problem: You are performing a new query for each shop, so if you have a million shops, it will be a million extra queries.
EDIT: (answering comment question)
Q: How can i sort $shops based on their latest product created_at field?
$sortedShops = $shops->sortBy(function ($shop, $key) {
return $shop->products->first()->created_at;
})->values()->all();
sortBy is called on the collection(not uery). It allows you to go over each element(in this case shops) and use each object. Please do note that this function will fail if you have no products linked to the shop.
The ->values()->all() at the end makes sure that when you convert your shops to json, you will create an array, and not an object in js.
Source:
https://laravel.com/docs/7.x/collections#method-sortby
EDIT: (deleted original answer as it did not work)
Previous answer does not work, because limit(3) will limit the total amound of products loaded, in stead of 3 products per shop(my bad).

Not sure if I am using eager loading correctly, getting null on a relationship that exists

I am having a tough time with this eloquent query and hoping someone can help me out.
The query works fine with the exception of the author relationship, it returns null when I know the author and the relationship both exist. (there are no books without authors in the database)
Here is the code in question:
// BookController.php
$categories = array(1,2,3,4);
$audience_age = 15;
$books = Book::with(array('categories','author'))->whereHas('categories', function($q) use ($categories, $audience_age)
{
$q->whereIn('id', $categories)->where('audience_age', '<=', $audience_age)->where('status', '=', 'active');
})->take(50)->get(array('id','data'));
// Book.php
public function author()
{
return $this->belongsTo('Author');
}
// Author.php
public function books()
{
return $this->hasMany('Book');
}
// authors migration
Schema::create('authors', function($table)
{
$table->increments('id');
$table->string('name');
$table->string('status');
$table->timestamps();
$table->softDeletes();
});
// books migration
Schema::create('books', function($table)
{
$table->increments('id');
$table->integer('author_id')->unsigned();
$table->foreign('author_id')->references('id')->on('authors');
$table->string('name');
$table->text('data');
$table->string('status');
$table->timestamps();
$table->softDeletes();
});
I verified that the relationship does exist and work by picking a particular Book, ID#40, and ran this query separately:
print_r(Book::find(40)->author->toJSON());
The author was found without issue when loading it that way.
I have been using my mad googl'in skills for a few hours trying to figure this one out but so far nothing.. any help would be very appreciated!
UPDATE --
After looking at DB::getQueryLog() I saw that the ID of the author in the query is set to 0
["query"]=> string(55) "select * from authors where authors.id in (?)" ["bindings"]=> array(1) { [0]=> int(0) } } }
The only problem with you code was not getting foreign key linking Book and Author:
$books = Book:: ... ->get(array('id','data'));
This way Eloquent doesn't know what to look for, thus it sets sensible default 0.
Simply enclose author_id in the select:
$books = Book:: ... ->get(array('id', 'data', 'author_id'));
and it will work as expected.
I did not figure out how to do it using the ORM, but I was able to get the query working by using DB::raw.
If I find out how to make it work within the ORM I will come back and update my answer.
For now, here is how I pulled it off:
$results = DB::select(DB::raw("SELECT b.id, b.data, au.id as author_id, GROUP_CONCAT(c.id) as categories
FROM books b
INNER JOIN book_category bc ON bc.book_id = b.id
INNER JOIN categories c ON c.id = bc.category_id
INNER JOIN authors au ON au.id = b.author_id
WHERE c.id IN (1,2,3,4)
LIMIT 2"));

Having problems making a query in one to many Relationship in Laravel 4

This is pretty weird and I have no idea what i'm doing wrong.
I have 2 models:
class Project extends Eloquent {
public function status()
{
return $this->belongsTo('ProjectStatus','status_id');
}
}
and
class ProjectStatus extends Eloquent {
protected $table = 'PStatus';
public function projects()
{
return $this->hasMany('Project');
}
}
The table "projects" has the proper foreign keys:
Schema::create('PStatus', function($table) {
$table->increments('id');
$table->string('name', 64);
$table->unique('name');
});
Schema::create('Projects', function($table) {
$table->increments('id');
$table->string('name', 100);
$table->integer('status_id')->unsigned();
$table->foreign('status_id')->references('id')
->on('PStatus')
->onDelete('cascade');
});
In the database (for example) I have only 1 project: "Project_1" (id = 1) with status_id = 1 (Lets say status name = "Open"). If I execute the following query:
$projects = Project::with(array('status' => function($query){
$query->where('name', '<>', 'Open');
}))->get();
I'm still getting the project in the results!!. This is the sql log:
array (size=3)
'query' => string 'select * from `PStatus` where `PStatus`.`id` in (?) and `name` <> ?' (length=67)
'bindings' =>
array (size=2)
0 => int 1
1 => string 'Open' (length=4)
'time' => float 0.36
if I print:
var_dump($projects->count());
I still get 1 project! How come?
I can easily solve my problem by changing the query to something like:
$projects = Project::where('status_id', '<>', 1)->get(); //assuming that status_id=1 => "Open"
but i prefer not to use ids directly when I guess the method with should work. What am I doing wrong here???
Actually this is the answer:
Eloquent Nested Relation with Some Constraint
A huge confusion with the with method. I should have used a join.

Resources