In my Laravel 8 application, I have the following models.
Company
protected $fillable = [
'id',
'name',
'website',
'company_logo',
'registration_number',
'date_established',
'address',
];
Employee
protected $fillable = [
'id',
'company_id',
'user_id',
'first_name',
'last_name',
'other_name',
'gender',
];
public function company()
{
return $this->belongsTo(Company::class,'company_id','id');
}
public function user()
{
return $this->belongsTo(User::class,'user_id','id');
}
I started this but got stuck on the way. I want to select company details based on the logged user. The company table should be the main table:
public function getMyCompany()
{
try {
$userId = Auth::user()->id;
$employeeId = Employee::where('user_id', $userId)->first();
$company = Company::...;
return $this->success('Company successfully Retrieved.', [
'company' => $company
]);
} catch (\Exception $e) {
Log::error($e);
return $this->error($e->getMessage(), $e->getCode());
}
}
How do I achieve this (select all details of the company) using:
$company = Company::...;
Making the main model
I'm not sure if you're expecting to get multiple companies from the user, or just a single one. The reason I'm not sure is that you have defined a 1-1 relationship between a company and an employee, yet it looks like you want getMyCompany() to return multiple companies.
If the idea is to retrieve only the one company that the employee works at, you can use the employee's "belongsTo"-relationship as following:
$company = $employee->company;
Since you have already retrieved the employee related to the authenticated user, and the employee model has a "company"-relationship.
If you want to do it in one go, you can chain the queries:
$company = Employee::where('user_id', Auth::user()->id)
->first()
->company;
Use Eloquent Eager loading for this as the Employee model has a belongsTo relationship for company
public function getMyCompany()
{
try {
$userId = Auth::user()->id;
$employee = Employee::with('company')->where('user_id',$userId)->first();
$company = $employee->company
return $this->success('Company successfully Retrieved.', [
'company' => $company
]);
} catch(\Exception $e) {
Log::error($e);
return $this->error($e->getMessage(), $e->getCode());
}
}
Refer: https://laravel.com/docs/8.x/eloquent-relationships#eager-loading for how Eager Loading works in Laravel
Related
I'm looking for some more information on how Laravel Sync Actually works. I have a Relationship Table that contains the following
group_id, user_id, permission, created_at, updated_at
I'm generating an array to pass to the sync command like so.
$groups_array[$group_id] = ['permission' => 0];
When I do this and then call sync it is setting the created_at and updated_at dates on existing relationships to NULL. The desired affect would be to have it just update the permission value to 0 on the existing relationship and not set the created_at and updated_at values to NULL.
It seems as if Sync is deleting everything and just re-creating the relationships based on the array sent. Is there not a way to tell this to update existing relationships vs. deleting and re-creating them?
By default Many to Many relationships in Laravel does not include timestamps, you need to define it on the relationship.
public function users()
{
return $this->belongsToMany(User::class)->withTimestamps();
}
// Post Model
public function categories()
{
return $this->belongsToMany('App\Category')->withTimestamps();
}
public function tags()
{
return $this->belongsToMany('App\Tag')->withTimestamps();
}
// Tag Model
public function posts()
{
return $this->belongsToMany('App\Post')->withTimestamps();
}
// Category Model
public function posts()
{
return $this->belongsToMany('App\Post')->withTimestamps();
}
// Post Controller Update
public function update(Request $request, Post $post)
{
$this->validate($request,[
'title' =>'required',
'categories' => 'required',
'tags' => 'required'
]);
$post->user_id = Auth::id();
$post->title = $request->title;
$post->slug = str_slug($request->title);
$post->save();
$post->categories()->sync($request->categories);
$post->tags()->sync($request->tags);
Toastr::success('Post Successfully Updated:)','Success');
return redirect()->route('admin.post.index');
}
// Delete
public function destroy(Post $post)
{
$post->categories()->detach();
$post->tags()->detach();
$post->delete();
Toastr::success('Post Successfully Deleted :)','Success');
return redirect()->back();
}
I have 3 Model
In Purchase Model
protected $fillable = [
'name', 'date', 'description', 'active', 'supplier', 'total', 'paid', 'purchase_status', 'payment_status',
];
public function product()
{
return $this->belongsTo(\App\Product::class);
}
public function order()
{
return $this->belongsTo(\App\Order::class);
}
In Order Model
protected $fillable = [
'name', 'quantity', 'unit_cost', 'discount', 'tax', 'sub_total'
];
public function order_items()
{
return $this->belongsTo(\App\OrderItem::class);
}
In my OrderItem Model
protected $fillable = ['active', 'description', 'quantity', 'discount', 'unit_price'];
public function order()
{
return $this->belongsTo(\App\Order::class);
}
Is it possible to query Order_Items From Purchases trough Orders Relationship in Laravel?
Oh, i found my answer...
In Order Model
i changed belongsTo to hasMany
protected $fillable = [
'name', 'quantity', 'unit_cost', 'discount', 'tax', 'sub_total'
];
public function order_items()
{
return $this->hasMany(\App\OrderItem::class);
}
Yes you can write this relations in your models and write the query like this:
Purchase
public function order()
{
return $this->belongsTo('App\Models\Order', 'order_id', 'id');
}
Order_Item
public function order()
{
return $this->belongsTo('App\Models\Order', 'order_id', 'id');
}
Order
public function order_items()
{
return $this->hasMany('App\Models\OrderItem', 'order_id', 'id');
}
public function purchases()
{
return $this->hasMany('App\Models\purchase', 'order_id', 'id');
}
Now you can use this query:
$purchase = \App\Models\Purchase::first();
$order_items = $purchase->order->order_items;
I see that you've fixed the relationship Order -> OrderItem of belongsTo to hasMany. But the question was also about how to query through relationships so I'll just add up to this so it could be useful to other users here.
Now you could do
$order_items = $purchase->order->order_items
to get the order items of the purchase.
However you should consider that this would execute further queries to the DB. One for fetching the order and then another one for fetching the order_items. If you're looping through a list of purchases, this could escalate quickly and end up making too much DB queries and affect the performance of your application.
The solution is eager loading.
You could do either pre-fetch the order and items along with the purchase like this:
$purchase = Purchase::with('order.order_items')->find(1);
or if you've already fetched the purchase, then you could do:
$purchase->load('order.order_items');
Then when getting the order items in your code like this
$purchase->order->order_items, you have no additional queries to the DB.
I have 2 models: customer and customerName. In my customer Controller I try to create a method that update fields from both tables. Any idea? Thanks!
CustomerController
public function update(Request $request, Customer $customer)
{
$customer = \App\CustomerName::where('customer_id', $customer->id)->first(); // if I remove this line I can update just "name" from first table
$data = $request->validate([
'name' => 'required|string', //is in customer migration
'first_name'=> 'required', //is in customerName migration
'last_name'=> 'required', //is in customerName migration
]);
$customer->update($data);
return response($customer,200);
}
Customer Model
class Customer extends Model
{
protected $fillable = ['name'];
public function customerName()
{
return $this->hasOne('App\CustomerName');
}
}
CustomerName Model
class CustomerName extends Model
{
protected $fillable = ['first_name', 'last_name'];
public function customer()
{
return $this->belongsTo('App\Customer');
}
}
Assuming customer always has record created for CustomerName, you should then use:
$customer->update(['name' => $data['name']);
$customer->customerName->update(\Arr::only($data, ['first_name', 'last_name']));
and additionally you should wrap this in database transaction like so:
\DB::transaction(function() use ($customer, $data) {
$customer->update(['name' => $data['name']);
$customer->customerName->update(\Arr::only($data, ['first_name', 'last_name']));
});
and of course you should remove this line:
$customer = \App\CustomerName::where('customer_id', $customer->id)->first(); // if I remove this line I can update just "name" from first table
because you should already have $customer object set using Route model binding.
Take a look at your code. You're overriding some variables by naming them the same thing:
public function update(Request $request, Customer $customer)
{
$customer = \App\CustomerName::where('customer_id', $customer->id)->first();
...
Before the line $customer = \App\CustomerName..., $customer is an instance of Customer. After that line, it is an instance of CustomerName, and you no longer have access to the Customer instance. Simply change you naming:
public function update(Request $request, Customer $customer)
{
$customerName = \App\CustomerName::where('customer_id', $customer->id)->first();
// Or, $customerName = $customer->customerName;
// You shouldn't have to query if your relationship is defined properly.
...
Next, save the values accordingly:
$customer->name = $request->input("name"); // or $data["name"]
$customer->save();
$customerName->first_name = $request->input("first_name"); // or $data["first_name"]
$customerName->last_name = $request->input("last_name"); // or $data["last_name"]
$customerName->save();
Set the values of $customer and $customerName accordingly, then call save() on both instances.
You're injecting the Customer instance, so you don't need to load it inside the function. Try this:
public function update(Request $request, Customer $customer)
{
$data = $request->validate([
'name' => 'required|string', //is in customer migration
'first_name'=> 'required', //is in customerName migration
'last_name'=> 'required', //is in customerName migration
]);
$customer->name = $data['name'];
$customer->customerName->first_name = $data['first_name'];
$customer->customerName->last_name = $data['last_name'];
$customer->push(); // This saves the model AND the related models as well.
return response($customer,200);
}
I have a Post/Category manyToMany relations and would like to be able to attach a default category named "Uncategorised" to each new post that is created. How can I do that? A BelongsToMany method only works on the Details page, not on Create page.
BelongsToMany::make(__('Categories'), 'categories', Category::class),
You can also set default value to your database field so that you can omit passing category and will be taken default to Uncategorised like if you are using MySQL you can do it this way by creating migration
$table->text('category')->default(0);
Because the BelongsToMany not show on mode create in Post Nova model. So we have to make our custom Select, by add this code to your fields:
public function fields(Request $request)
{
if($request->editMode=="create"){
$categories = \App\Category::get(['id','name']);
$options = [];
foreach($categories as $value){
$options[$value->id] = $value->name;
}
return [
ID::make()->sortable(),
Text::make('Title'),
Text::make('Summary'),
Textarea::make('Content'),
Select::make('Categories', 'category_id')
->options($options)
->displayUsingLabels()
->withMeta(['value' => 1]) // 1 = id of Uncategorised in categories table
];
}
return [
ID::make()->sortable(),
Text::make('Title'),
Text::make('Summary'),
Textarea::make('Content'),
BelongsToMany::make('Categories','categories')->display('name'),
];
}
Don’t forget relationship function in both, Post and Category model:
class Post extends Model
{
public function categories(){
return $this->belongsToMany(Category::class, 'category_post', 'post_id', 'category_id');
}
}
And:
class Category extends Model
{
public function posts(){
return $this->belongsToMany(Post::class,'category_post', 'category_id', 'post_id');
}
}
Then, custom the function process the data on mode Create of Post resource page, it’s at nova\src\Http\Controllers\ResourceStoreController.php, change function handle to this:
public function handle(CreateResourceRequest $request)
{
$resource = $request->resource();
$resource::authorizeToCreate($request);
$resource::validateForCreation($request);
$model = DB::transaction(function () use ($request, $resource) {
[$model, $callbacks] = $resource::fill(
$request, $resource::newModel()
);
if ($request->viaRelationship()) {
$request->findParentModelOrFail()
->{$request->viaRelationship}()
->save($model);
} else {
$model->save();
// your code to save to pivot category_post here
if(isset($request->category_id)&&($resource=='App\Nova\Post')){
$category_id = $request->category_id;
$post_id = $model->id;
\App\Post::find($post_id)->categories()->attach($category_id);
}
}
ActionEvent::forResourceCreate($request->user(), $model)->save();
collect($callbacks)->each->__invoke();
return $model;
});
return response()->json([
'id' => $model->getKey(),
'resource' => $model->attributesToArray(),
'redirect' => $resource::redirectAfterCreate($request, $request->newResourceWith($model)),
], 201);
}
}
All runs well on my computer. A fun question with me! Hope best to you, and ask me if you need!
What I ended up doing was saving the data on Post Model in boot().
public static function boot()
{
parent::boot();
static::created(function (Post $post) {
$post->categories()->attach([1]);
});
}
My product model like this :
<?php
...
class Product extends Model
{
...
protected $fillable = ['name','photo','description',...];
public function favorites(){
return $this->morphMany(Favorite::class, 'favoritable');
}
}
My favorite model like this :
<?php
...
class Favorite extends Model
{
...
protected $fillable = ['user_id', 'favoritable_id', 'favoritable_type'];
public function favoritable()
{
return $this->morphTo();
}
}
My eloquent query laravel to add, delete and get like this :
public function addWishlist($product_id)
{
$result = Favorite::create([
'user_id' => auth()->user()->id,
'favoritable_id' => $product_id,
'favoritable_type' => 'App\Models\Product',
'created_at' => Carbon::now()
]);
return $result;
}
public function deleteWishlist($product_id)
{
$result = Favorite::where('user_id', auth()->user()->id)
->where('favoritable_id', $product_id)
->delete();
return $result;
}
public function getWishlist($product_id)
{
$result = Favorite::where('user_id', auth()->user()->id)
->where('favoritable_id', $product_id)
->get();
return $result;
}
From the code above, I'm using parameter product_id to add, delete and get data favorite
What I want to ask here is : Whether the above is the correct way to add, delete and get data using polymorphic relationship?
Or is there a better way to do that?