eloquent with multiples tables data and relationship - php

I have these 3 tables:
Schema::create('companies', function (Blueprint $table) {
$table->increments('id');
$table->integer('city_id')->unsigned();
$table->string('name');
$table->string('address');
$table->float('lat', 10,6);
$table->float('lng', 10,6);
$table->timestamps();
$table->foreign('city_id')->references('id')->on('cities');
});
Schema::create('company_clients', function (Blueprint $table) {
$table->increments('id');
$table->integer('company_id')->unsigned();
$table->integer('client_id')->unsigned();
$table->foreign('company_id')->references('id')->on('companies');
$table->foreign('client_id')->references('id')->on('companies');
});
Schema::create('cities', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
});
Now, I want to have an eloquent query, where it will return an array (Not only one item) of companies, where the (for example)company_id=1 on company_clients table. Also, the city_id is suppose to return the name and not the id, using the cities table. I cannot imagine how to do it right now.
I made:
class City extends Model
{
protected $table = 'cities';
public $timestamps = false;
protected $fillable = [
'name',
];
public function companies()
{
return $this->hasMany('App\Company', 'city_id', 'id');
}
}
class CompanyClients extends Model
{
protected $table = 'company_clients';
public $timestamps = false;
protected $fillable = [
'company_id', 'client_id',
];
public function companies()
{
return $this->belongsTo('App\Company', 'company_id', 'id');
}
public function clients()
{
return $this->belongsTo('App\Company', 'company_id', 'id');
}
}
class Company extends Model
{
protected $table = 'companies';
protected $fillable = [
'name', 'address', 'lat', 'lng', 'city_id',
];
protected $hidden = [
'clients', 'created_at', 'updated_at',
];
public function city()
{
return $this->belongsTo('App\City', 'city_id', 'id');
}
public function companies()
{
return $this->hasMany('App\CompanyClients', 'company_id', 'id');
}
public function clients()
{
return $this->hasMany('App\CompanyClients', 'client_id', 'id');
}
}
But, I'm missing the code in the controller. I tried:
$result = Company::leftJoin('company_clients', function($join) {
$join->on('companies.id', '=', 'company_clients.company_id');
})->where('company_clients.company_id', '=', 1 )->get();
or
$result = Company::with(['clients' => function($q){
$q->where('company_id', 1);
}])->get();
but is not returning the correct result. What I'm missing?
Thanks!
EDITED:
I had found a way, but I'm not sure if is the best way to do it. Can someone please confirm?
$result = Company::join('company_clients', function($join) {
$user = Auth::guard('api')->user();
$join->on('companies.id', '=', 'company_clients.client_id')->where('company_clients.company_id', '=', $user->company_id );
})->join('cities', 'cities.id', '=', 'companies.city_id')->get(array('companies.*', 'cities.name'));

Try
CompanyClients::with('company.city', 'clients')->where('company_id', 1)->get();
Rename
companies
relationship on the CompanyClients model to
company

Related

Get all the genres that a movie belongs to in the Laravel REST API

Asside from the $movie data I want to also get all the genres that a movie belongs to following this path: 127.0.0.1:8000/api/movies/{id}. My foreign keys are in the separate table so how exactly I can achieve that? My migrations:
public function up()
{
Schema::create('genres', function (Blueprint $table) {
$table->id();
$table->string('name');
});
}
public function up()
{
Schema::create('movies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('status')->nullable()->default(0);
$table->string('image_path')->default('default.png');
});
}
public function up()
{
Schema::create('genre_movie', function (Blueprint $table) {
$table->foreignId('genre_id')->constrained()->cascadeOnDelete();
$table->foreignId('movie_id')->constrained()->cascadeOnDelete();
});
}
Movie model:
class Movie extends Model
{
use HasFactory;
public $timestamps = false;
protected $fillable = ['name', 'status', 'image_path'];
public function genres()
{
return $this->belongsToMany(Genre::class, 'genre_movie');
}
}
Genre model:
class Genre extends Model
{
use HasFactory;
public $timestamps = false;
protected $fillable = ['name'];
public function movies()
{
return $this->belongsToMany(Movie::class, 'genre_movie');
}
}
Movie Resource:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'status' => $this->status,
'image_path' => url()->to('/images/' .$this->image_path),
];
}
Genre Resource:
public function toArray($request){
return [
'id' => $this->id,
'name' => $this->name,
];
}
Function in the controller which only returns $movie data:
public function show(Movie $movie)
{
return new MovieResource($movie);
}
I thought this would work:
public function show(Movie $movie)
{
return new MovieResource($movie->with('genres'));
}
But I receive this error: "message": "Property [id] does not exist on the Eloquent builder instance."
You can return JSON with a small change too.
public function showMovieAndGenre(Movie $movie)
{
$fullData = Movie::join('genres', 'genres.id', '=', 'movies.id')
->select([
'movies.id AS mid',
'genres.id AS gid',
'movies.name AS mname',
'genres.name AS gname',
'movies.status AS status',
'movies.image_path AS image_path'
])
->get();
return $fullData;
}
return $this->belongsToMany(Genre::class, 'genre_movie');
Consider using hasManyThrough https://laravel.com/docs/9.x/eloquent-relationships#has-many-through

Show product based on selected category in Laravel 5.8?

The below code is a list of my categories (product_categories), list of products (products) and categories assigned to products (product_selected_categories).
I would like to display my product along with the category to which it was added.
I'm trying to do this using the following code:
Product::findOrFail($id)
But this code does not work. The code displays only the product without information about the category to which the product belongs.
How do I fix this?
My project is in Laravel 5.8.
Migration
Schema::create('product_categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('company_id')->unsigned();
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->char('enable', 1)->default(0);
$table->string('name', 85)->nullable();
$table->string('url_address', 160);
$table->integer('level')->default(0);
//$table->bigInteger('parent_id')->default(0);
//$table->bigInteger('parent_id')->nullable();
$table->unsignedBigInteger('parent_id')->nullable();
$table->foreign('parent_id')->references('id')->on('product_categories')->onDelete('cascade');
$table->bigInteger('number')->default(0);
$table->timestamps();
$table->engine = "InnoDB";
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
});
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('company_id')->unsigned();
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
//$table->bigInteger('category_id')->unsigned();
//$table->foreign('category_id')->references('id')->on('product_categories');
$table->smallInteger('id_delivery_vat')->unsigned();
$table->foreign('id_delivery_vat')->references('id')->on('vat');
$table->smallInteger('id_product_vat')->unsigned();
$table->foreign('id_product_vat')->references('id')->on('vat');
$table->bigInteger('id_producer')->unsigned();
//$table->foreign('id_producer')->references('id')->on('product_producers');
$table->string('name', 120)->nullable();
$table->string('qr_code', 120)->nullable();
$table->string('oe_code', 120)->nullable();
$table->char('enable', 1)->default(0);
$table->char('promo', 1)->default(0);
$table->longText('description')->nullable();
$table->decimal('product_price', 9, 2)->default(0);
$table->decimal('promo_product_price', 9, 2)->default(0);
$table->decimal('product_delivery_price', 9, 2)->default(0);
$table->unsignedInteger('quantity')->default(0);
$table->string('url_address', 160);
$table->timestamps();
$table->engine = "InnoDB";
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
});
Schema::create('product_selected_categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('category_id')->unsigned()->index();
$table->foreign('category_id')->references('id')->on('product_categories');
$table->bigInteger('subcategory1_id')->default(0);
$table->bigInteger('parent_subcategory1_id')->default(0);
$table->bigInteger('subcategory2_id')->default(0);
$table->bigInteger('parent_subcategory2_id')->default(0);
$table->bigInteger('subcategory3_id')->default(0);
$table->bigInteger('parent_subcategory3_id')->default(0);
$table->bigInteger('subcategory4_id')->default(0);
$table->bigInteger('parent_subcategory4_id')->default(0);
$table->bigInteger('subcategory5_id')->default(0);
$table->bigInteger('parent_subcategory5_id')->default(0);
$table->bigInteger('subcategory6_id')->default(0);
$table->bigInteger('parent_subcategory6_id')->default(0);
$table->bigInteger('subcategory7_id')->default(0);
$table->bigInteger('parent_subcategory7_id')->default(0);
$table->bigInteger('subcategory8_id')->default(0);
$table->bigInteger('parent_subcategory8_id')->default(0);
$table->bigInteger('subcategory9_id')->default(0);
$table->bigInteger('parent_subcategory9_id')->default(0);
$table->bigInteger('product_id')->unsigned()->index();
$table->foreign('product_id')->references('id')->on('products');
});
Models
class Product extends Model
{
use scopeActiveTrait;
protected $fillable = ['company_id', 'id_delivery_vat', 'id_product_vat', 'id_producer', 'name', 'qr_code', 'oe_code', 'enable', 'promo', 'description', 'product_price', 'promo_product_price', 'product_delivery_price', 'quantity', 'url_address'];
protected $quarded = ['id'];
public $timestamps = true;
public function categories()
{
return $this->belongsToMany(Category::class, 'product_selected_categories', 'product_id', 'category_id');
}
}
class ProductCategory extends Model
{
use scopeActiveTrait;
protected $guarded = ['id'];
protected $fillable = ['company_id', 'enable', 'name', 'url_address', 'level', 'parent_id', 'number'];
public $timestamps = true;
//protected $table = 'products_category';
public function parent()
{
return $this->belongsTo('App\ProductCategory', 'parent_id', 'id');
}
public function children()
{
return $this->hasMany('App\ProductCategory', 'id', 'parent_id');
}
public function products()
{
return $this->belongsToMany(Product::class, 'product_selected_categories', 'category_id', 'product_id');
}
}
class ProductSelectedCategory extends Model
{
protected $quarded = ['id'];
protected $fillable = ['subcategory1_id', 'parent_subcategory1_id', 'category_id', 'subcategory2_id', 'parent_subcategory2_id', 'subcategory3_id', 'parent_subcategory3_id', 'subcategory4_id', 'parent_subcategory4_id', 'subcategory5_id', 'parent_subcategory5_id', 'subcategory6_id', 'parent_subcategory6_id', 'subcategory7_id', 'parent_subcategory7_id', 'subcategory8_id', 'parent_subcategory8_id', 'subcategory9_id', 'parent_subcategory9_id', 'product_id' ];
public $timestamps = false;
}
If you know that you will need the Product's Categories, you can eager load related models by using the QueryBuilder's with() function:
$product = Product::with('categories')->findOrFail($id);
If you have the need to eager load multiple relationships, you can pass an array to with():
$product = Product::with(['categories', ...])->findOrFail($id);
If, however, you may not need the Product's Categories, you can lazy load the categories with the load() function:
$product = Product::findOrFail($id);
// ... some time later you end up needing the Categories...
$product->load('categories');
Here again, you may pass an array to load() if needed:
$product->load(['categories', ...]);
Another option apart from those listed above would be to define a $with property on your Product model:
class Product extends Model
{
use scopeActiveTrait;
protected $fillable = ['company_id', 'id_delivery_vat', 'id_product_vat', 'id_producer', 'name', 'qr_code', 'oe_code', 'enable', 'promo', 'description', 'product_price', 'promo_product_price', 'product_delivery_price', 'quantity', 'url_address'];
protected $quarded = ['id'];
public $timestamps = true;
protected $with = ['categories'];
...
}
This is another way to use eager loading. It should only be used if you know that whenever you load a product (or products), you will always need every loaded product's categories. (see "Eager loading by default" here)
You can load the relation in this way :
Product::findOrFail($id)->with('categories');

fetch data for all users in same group

My code works. But I'm not sure is it a best solution. I need option to display user players and / or players from a group user belong to. Thank you for you time.
$user = $request->user();
$userGroups = $user->groups;
$friendsPlayers = [];
foreach ($userGroups as $group) {
$groupUsers = $group->users;
foreach ($groupUsers as $groupUser) {
if ($groupUser->id !== $user->id) {
$userPlayer = $groupUser->players;
foreach ($userPlayer as $player) {
if (!in_array($player, $friendsPlayers)) {
$friendsPlayers[] = $player;
}
}
}
}
}
1.Schema/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Schema::create('groups', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('info');
$table->timestamps();
});
Schema::create('players', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned()->index();
$table->string('name');
$table->string('url');
$table->string('type');
$table->integer('wins');
$table->integer('lost');
$table->integer('draws');
$table->timestamps();
});
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('group_user', function (Blueprint $table) {
$table->integer('group_id')->unsigned()->nullable();
$table->foreign('group_id')->references('id')
->on('groups')->onDelete('cascade');
$table->integer('user_id')->unsigned()->nullable();
$table->foreign('user_id')->references('id')
->on('users')->onDelete('cascade');
$table->timestamps();
});
2.Models/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Group extends Model
{
protected $fillable = [
'name', 'info',
];
public function users() {
return $this->belongsToMany(User::class);
}
}
class Player extends Model
{
protected $guarded = [];
public function user() {
return $this->belongsTo(User::class);
}
public function leagues() {
return $this->belongsToMany(League::class)->withPivot('win', 'lost',
'draw')->withTimestamps();
}
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
public function players() {
return $this->hasMany(Player::class);
}
public function leagues() {
return $this->hasMany(League::class);
}
public function scoreboards() {
return $this->hasMany(Scoreboard::class);
}
public function groups() {
return $this->belongsToMany(Group::class);
}
}
This could be a perfect use case to use Laravel Collection.
$result = $user->load('groups.groupUsers.players')
->groups
->map->groupUsers
->collapse()
->filter(function ($groupUser) use ($user) {
return $groupUser->isNot($user);
})
->unique('id')
->map->players
->collapse()
->unique('id');
A different approach is using a query to get the result.
First, lets create a sub query to get all the groups the user joined.
$groupsJoinedByUser = Group::whereHas('users', function ($query) use ($user) {
$query->whereKey($user->id);
});
We can also create that sub-query this way:
$groupsJoinedByUser = Group::select('groups.*')
->join('group_user', 'groups.id', '=', 'group_user.group_id')
->where('group_user.user_id', $user->id);
Now we can create the query to get the players:
$players = Player::select('players.*')
->join('users', 'players.user_id', '=', 'users.id')
->join('group_user', 'users.id', '=', 'group_user.user_id')
->joinSub($groupsJoinsByUser, 'groups_joined_by_user', function($join) {
$join->on('group_user.group_id', '=', 'groups_joined_by_user.id')
})
->where('users.id', '!=', $user->id);
->distinct()
->get();

Laravel implementing belongsTo one product for one or some category

in my database i have product_category and products that one product maybe belongs to one or some category in product_category table, now my question is: when user on submitting product with one or some category how can i save that on database to have for example one category have one or some product?
in view i have multiple select as:
{{ Form::select('categories[]', $productCategories, null, array('class' => 'multiselect-success multiselect_selected','multiple'=>'multiple')) }}
products model:
class Products extends Model
{
protected $table = 'products';
protected $guarded = ['id'];
protected $casts = [
'images' => 'array'
];
public function productCategories()
{
return $this->belongsTo(ProductCategories::class);
}
}
productCategories model:
class ProductCategories extends Model
{
protected $table = 'product_categories';
protected $guarded =['id'];
protected $casts=[
'images'=>'array'
];
public function products()
{
return $this->hasMany(Products::class);
}
}
and store function into controller:
public function store(RequestProducts $request)
{
try {
$data = Products::create([
'name' => $request->name,
'amount' => $request->amount,
'product_code' => $request->product_code,
'weight' => $request->weight
/* MY PROBLEM IS HERE */
'category_id' => $request->categories
]);
} catch (Exception $ex) {
...
}
return redirect(route('manageProductCategories.index'));
}
in html view categories is an array and how can i implementing that?
UPDATE
after update code with createMany i get this error:
General error: 1364 Field 'category_id' doesn't have a default value (SQL: insert into `products` (`name`, `lang`, `amount`, `product_code`, `weight`, `images`, `updated_at`, `created_at`) values (eqweqwe, fa, 45,000, asd, asdasd, '', 2017-12-09 04:45:44, 2017-12-09 04:45:44))
migration files:
public function up()
{
Schema::create('product_categories', function (Blueprint $table) {
$table->increments('id');
$table->string('category_name');
$table->string('lang', 2);
$table->text('images');
$table->timestamps();
});
}
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('amount');
$table->string('product_code');
$table->string('weight');
$table->string('lang', 2);
$table->text('images');
$table->integer('category_id')->unsigned()->index();
$table->foreign('category_id')->references('id')->on('product_categories')->onDelete('cascade');
$table->timestamps();
});
}
From your question and comments, I understand the following.
Many products may have the category "category_1" and "product_1" may belongs to many categories.
To implement this you have to use "Many To Many" relationship.
I have updated your code, this might help you.
Migrations:
public function up()
{
Schema::create('product_categories', function (Blueprint $table) {
$table->increments('id');
$table->string('category_name');
$table->string('lang', 2);
$table->text('images');
$table->timestamps();
});
}
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('amount');
$table->string('product_code');
$table->string('weight');
$table->string('lang', 2);
$table->text('images');
$table->timestamps();
});
}
public function up()
{
Schema::create('products_product_category', function (Blueprint $table) {
$table->integer('product_id');
$table->integer('product_category_id');
});
}
Models
products model:
class Products extends Model
{
protected $table = 'products';
protected $guarded = ['id'];
protected $casts = [
'images' => 'array'
];
public function productCategories()
{
return $this->belongsToMany(ProductCategories::class,'products_product_category');
}
}
productCategories model:
class ProductCategories extends Model
{
protected $table = 'product_categories';
protected $guarded =['id'];
protected $casts=[
'images'=>'array'
];
public function products()
{
return $this->belongsToMany(Products::class, 'products_product_category');
}
}
Controller
public function store(RequestProducts $request)
{
try {
$data = Products::create([
'name' => $request->name,
'amount' => $request->amount,
'product_code' => $request->product_code,
'weight' => $request->weight
]);
$data->productCategories()->sync($request->categories);
} catch (Exception $ex) {
...
}
return redirect(route('manageProductCategories.index'));
}
Hope it will helps..

Bring the latest commented topic on top on forum index page

I am building a forum. I want to bring published topics on top whenever users leave a reply. For topics without replies, I want to order them by created_at column.
How do you do that?
Forum controller
public function index()
{
$categories = Category::all();
$topics = Topic::with(['comments' => function ($query) {
$query->orderBy('comments.created_at', 'desc');
}])->paginate(20);
}
Here is my topic table
Schema::create('topics', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->string('title');
$table->text('body');
$table->timestamps();
});
Here is my comment table
$table->increments('id');
$table->text('reply');
$table->integer('user_id')->unsigned();
$table->integer('topic_id')->unsigned();
$table->foreign('topic_id')->refrenced('id')->on('topics')->onDelete('cascade');
$table->timestamps();
Comments model
class Comment extends Model
{
protected $fillable = [
'reply',
'user_id',
'topic_id'
];
public function topic()
{
return $this->belongsTo('App\Topic');
}
public function user()
{
return $this->belongsTo('App\User');
}
}
Topic model
class topic extends Model
{
protected $fillable = [
'title',
'body',
'category_id'
];
public function category()
{
return $this->belongsTo('App\category');
}
public function user()
{
return $this->belongsTo('App\User');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
}
still trying to figure this out. any help will be hugely appreciated!!
Try using eager load with constraint:
public function index()
{
$categories = Category::all();
$topics = Topic::with(['comments' => function ($query) {
$query->orderBy('created_at', 'desc');
}])->paginate(20);
return view('forums.index',compact('categories','topics'));
}
DB::table('topics')
->leftjoin('comments', function($join) {
$join->on('topics.id', '=', 'comments.topic_id');
})
->select(DB::raw('IF(comments.id IS NULL,0, 1) as topic_comment'))
->orderBy('topic_comment', 'DESC')
->orderBy('topics.created_at', 'DESC')
->get();
the topic_comment as 1 records you can show on top right and rest wherever you want

Categories