Sync function in laravel not working as expected - php

I have an edit form which should update an activity with selected categories, but the sync function isn't working as expected.
It adds the selected checkboxes to the database but doesn't delete the old values. Resulting in mutliple of the same values in the pivot table.
form code:
<p class="activiteit-categorie">
<label>categorie</label>
#foreach ($categories as $categorie)
<div class="categorie-input">
<input type="checkbox" name="categories[]" value="{{ $categorie->id }}"><label for="categorie">{{$categorie->name}}</label><br>
</div>
#endforeach
</p>
controller code:
if ($request->has('categories')) {
$activiteit->categorie()->sync($request->input('categories'));
} else {
$activiteit->categorie()->detach($request->input('categories'));
}
return redirect()->route('active-overview')->with('message', 'Activiteit is succesvol aangepast!');
models categorie:
namespace App;
use Illuminate\Database\Eloquent\Model;
class categorie extends Model
{
protected $fillable = [
'name'
];
public function activiteit() {
return $this->BelongsToMany('App\activiteit');
}
}
model activiteit:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class activiteit extends Model
{
protected $fillable = [
'name', 'image', 'intro', 'goal', 'traject', 'extra_info', 'duration', 'price'
];
public function categorie() {
return $this->belongsToMany('App\categorie');
}
}

You can try this
class categorie extends Model{
protected $fillable = [
'name'
];
public function activiteit() {
return $this->BelongsToMany('App\activiteit','pivot_table_name','categorie_id','activiteit_id');
}
}
Use like this another Model
if ($request->has('categories')) {
$activiteit->categorie()->sync($request->categories);
} else {
$activiteit->categorie()->detach($request->categories);
}
It works fine for me

So after trying some stuff out I came up with a simple solution to my problem.
I first detach all the categories from the selected activity and then add selected categories to the pivot table.
$activiteit->categorie()->detach();
if ($request->has('categories')) {
$activiteit->categorie()->sync($request->categories);
}

Related

How can I get a pivot on my relationship in Laravel?

I have a relationship in my app. A candidate can have several "candidate_trainings" and each "candidate_training" is associated with a training. I wanted to avoid making "candidate_trainings" the piviot since it's hard to delete the right values when detaching, etc. So, how can I, on my hasMany relationship get the CandidateTraining model with the data from the Training model.
Here are my relationships:
<?php
namespace App;
use App\Traits\SanitizeIds;
use App\Salary;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class Candidate extends Model
{
public function saveTraining($data) {
$this->candidateTrainings()->delete();
foreach(json_decode($data['training']) as $training) {
if(Training::find($training->training)->first()) {
$candidateTraining = new CandidateTraining;
$candidateTraining->description = $training->value;
$candidateTraining->training_id = $training->training;
$this->candidateTrainings()->save($candidateTraining);
}
}
}
public function candidateTrainings() {
return $this->hasMany('\App\CandidateTraining');
}
public function trainings() {
return $this->belongsToMany('\App\Training');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Training extends Model
{
protected $fillable = ['name_french', 'name_english'];
public function candidates() {
return $this->belongsToMany('\App\Candidate');
}
public function candidateTrainings() {
return $this->hasMany('\App\CandidateTraining');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class CandidateTraining extends Pivot
{
public function candidate() {
return $this->belongsTo('\App\Candidate');
}
public function training() {
return $this->belongsTo('\App\Training');
}
}
Thank you!
For you to be able to update the data directly on the CandidateTraining model, you need to add the $fillable fields to it.
protected $fillable = ['training_id', 'description'];
Your code should work! But if you don't mind, I did a little refactoring. You can accomplish this in another way:
<?php
namespace App;
use App\Traits\SanitizeIds;
use App\Salary;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class Candidate extends Model
{
public function saveTraining($data)
{
// remove all relationships
$this->trainings()->detach();
// add new ones
foreach(json_decode($data['training']) as $training)
{
if(Training::find($training->training)->first())
{
$this->trainings()->attach($training->training, [
'description' => $training->value,
]);
}
}
}
public function candidateTrainings()
{
return $this->hasMany(App\CandidateTraining::class);
}
public function trainings()
{
return $this->belongsToMany(App\Training::class)
->withTimestamps()
->using(App\CandidateTraining::class)
->withPivot([
'id',
'training_id',
'description',
]);
}
}
That $training->training stuff is not readable, change it to something like $training->id if you are able to.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Training extends Model
{
protected $fillable = ['name_french', 'name_english'];
public function candidates()
{
return $this->belongsToMany(App\Candidate::class)
->withTimestamps()
->using(App\CandidateTraining::class)
->withPivot([
'id',
'training_id',
'description',
]);;
}
public function candidateTrainings()
{
return $this->hasMany(App\CandidateTraining::class);
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class CandidateTraining extends Pivot
{
protected $fillable = ['training_id', 'description'];
public function candidate()
{
return $this->belongsTo(App\Candidate::class);
}
public function training()
{
return $this->belongsTo(App\Training::class);
}
}
If you want to access the pivot object from a controller:
$candidates = Candidate::with(['trainings'])->get();
foreach ($candidates as $candidate)
{
dd($candidate->pivot);
}

App\Todo::lead must return a relationship instance, but "null" was returned. Was the "return" keyword used?

I have Todo and Tbl_leads model and there corresponding tables todos and tbl_leads. When I am trying todo have the lead name it throw me an error.
#This is Tbl_leads model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tbl_leads extends Model
{
//Table Name
protected $table = 'tbl_leads';
//Primary key
public $primaryKey = 'ld_id';
//Timestamps
public $timestamps = true;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'ld_id', 'first_name', 'last_name', 'email',
];
public function tasks() {
$this->hasMany('App\Todo', 'lead_id','ld_id');
}
}
This is Todo Model
<?php
namespace App;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Todo extends Model
{
use SoftDeletes;
protected $fillable = [
'title',
'description',
'user_id',
'outcome_id',
'lead_id',
'tasktype_id',
'due_time',
'complete_time',
];
protected $casts = [
'due_time' => 'datetime',
'complete_time',
];
public function lead() {
$this->belongsTo('App\Tbl_leads', 'lead_id');
}
}
This is my index method
public function index()
{
$tasks = Todo::latest()->paginate(5);
return view('taskmanagement.cruds.index',compact('tasks'));
}
this is blade where I want get the the first name from Tbl_leads model
div class="card-body">
<ul class="todo-list" data-widget="todo-list">
#forelse($tasks as $task)
<div class="col-3">
#if(!empty($task->lead_id))
<div>
<small>Related to</small>
</div>
<div class="mt-0">
<a href="#">
<strong class="mx-4">{{$task->lead->first_name}}</strong>
</a>
</div>
#endif
</div>
</div>
</div>
</li>
#empty
<p class="text-center">No Tasks is available</p>
#endforelse
Plz can anyone tells me what I have done wrong. And I know the model name and there primary key slightly different is not set accordingly to laravel. Is my model relationship correct?
Let me clarify few points to you. Firstly you need to put a return in each relationship. For example:
public function lead() {
return $this->belongsTo('App\Tbl_leads', 'lead_id');
}
Next thing you are showing this portion {{$task->lead->first_name}} which is called lazy loading. It means if you are displaying 100 records of Todo then you are querying database 101 times. 1 for Todo and 100 times for lead->first_name. Which is not good. So what you can do in your index method pass your relation in with() so that it will be eager loaded. Means it will become just one or two query or simply a join. So it will be fast. Example of your index method...
public function index()
{
$tasks = Todo::with('lead')->latest()->paginate(5);
return view('taskmanagement.cruds.index',compact('tasks'));
}

Append new element to eloquent collection

I have two tables posts and photos. Each post has 5 photos. I want to list in view each post with one photo (profile pic), first picture.
$published_post = Post::where('created_by',auth()->user()->id)
->where('status','published')
->get();
$photo = Photo::where('post',$published_post->id)->get();
These two gives me two different collection. How can I add the first photo of a particular post to its array so in view I can display using a foreach loop.
This is how I want in view:
#foreach($published_post as $item)
{{ $item->title }}
{{ $item->profile_photo }}
#endforeach
I tried put and push, but doesn't seem to be working. Not sure how exactly does we append a new key value pair to an object.
My two models:
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->timestamps();
});
Schema::create('photos', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('image');
$table->integer('post');
$table->timestamps();
});
class Post extends Model
{
protected $table = 'posts';
}
class Photo extends Model
{
protected $table = 'photos';
protected $fillable = ['image', 'post'];
}
Post Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/*This is used to specify the table which this model is associated with*/
protected $table = 'posts';
protected $fillable = [
'title'
];
public $timestamps = true;
public function photos(){
return $this->hasMany(Photos::class,'post');
//post is the foreign key for posts table
}
}
Photo Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Photo extends Model
{
/*This is used to specify the table which this model is associated with*/
protected $table = 'photos';
protected $fillable = [
'image', 'post'
];
public $timestamps = true;
}
View:
#foreach($published_post as $item)
{{ $item->title }}
{{ $item->photos->first()->image }} // photos relation is invoked and fetched first image
#endforeach
You need to create 2 Models, one for Posts and one for Photos.
php artisan make:model Post
php artisan make:model Photo
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Posts extends Model
{
//
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Photo extends Model
{
//
}
Then create a hasMany relationship on the Post model to link to the Photo model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Photo;
class Post extends Model
{
public function photos()
{
return $this->hasMany(Photo::class);
}
}
Then in your view you can lazy load the photos whenever you like
#foreach($posts as $post)
{{ $post->title }}
{{ $post->photo[0]->name}}
#endforeach
The syntax to go in your view will be slightly different, but this gives you a good idea on how the functionality should work.
Ok, first you should change your Post model like this:
class Post extends Model
{
protected $table = 'posts';
public function photos()
{
return $this->hasMany(Photo::class, 'post');
}
}
And then, add the following to your Photo model:
class Photo extends Model
{
protected $table = 'photos';
public function post()
{
return $this->belongsTo(Post::class, 'post');
}
}
With this, you've created the relation between your models, and now you can get your data this way:
$published_post = Post::where('created_by',auth()->user()->id)
->where('status','published')
->with('photos')
->get();
And in your view, you can get the first photo this way:
#foreach($published_post as $item)
{{ $item->title }}
{{ $item->photos()->first()->name }}
#endforeach
For more info on relations, you might want to read the docs.
I hope this helps!

Logical complex insert into 2 following tables - Laravel

I have a house management project and i am trying to execute a complex function that consists of:
Each time i insert a new Bill into the DB, another table will be filled creating a new row for each share of this bill according to the number of flatmates this house has. I managed to insert into the Bills table but the program returns me to the expected page but with no insert into the Shares table. Not sure if logically i'm doing alright. The code bellow is how i tried to retrieve information of the last insert into the Bills table, which should then have its objects properties used into the Shares table. Does someone have any clue on how i can i proceed?
This is my controller function:
public function store(Request $request){
$bill = Bill::create($request->all());
$users = User::where('house_id', Auth::user()->house->id);
$nflatmates = Auth::user()->house->nflatmates;
$shared_amount = $bill->bill_amount / $nflatmates;
foreach($users as $user){
$data = ['user_id'=>$user->id,
'bill_id'=>$bill->id,
'share_amount'=>$shared_amount];
Share::create($data);
}
return redirect('/admin/bills');
}
This is my form blade. I believe the problem doesnt come from here. Just in case.
{!! Form::open(['method'=>'post', 'action'=>'AdminBillsController#store']) !!}
<div class="form-group' has-error' : '' }}">
<div class="col-md-6">
{!! Form::text('description',null,['class'=>'form-control', 'placeholder'=>'Bill Description']) !!}
</div>
</div>
<div class="form-group' has-error' : '' }}">
<div class="col-md-6">
{!! Form::number('bill_amount',null,['class'=>'form-control', 'placeholder'=>'Amount', 'required|between:0,99.99']) !!}
</div>
</div>
<input type="hidden" name="house_id" value="{{Auth::user()->house->id}}">
<br>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Register
</button>
</div>
</div>
{!! Form::close() !!}
These are my relationships:
This is the Share Model
<?php
namespace App;
use App\User;
use App\Bill;
use Illuminate\Database\Eloquent\Model;
class Share extends Model{
protected $fillable = [
'id', 'user_id', 'bill_id', 'share_amount', 'share_status'
];
public function user(){
return $this->belongsTo('App\User');
}
public function bill(){
return $this->belongsTo('App\Bill');
}
}
And this is the Bill Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\House;
use App\User;
use App\Share;
class Bill extends Model{
protected $fillable = [
'id', 'description', 'bill_amount', 'house_id'
];
public function house(){
return $this->belongsTo('App\House');
}
public function share(){
return $this->hasMany('App\Share');
}
}
This is the User Model:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\House;
use App\Role;
use App\Task;
use App\Share;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'id','name', 'email', 'is_active','house_id','role_id','password',
];
protected $hidden = [
'password', 'remember_token',
];
public function house(){
return $this->belongsTo('App\House');
}
public function role(){
return $this->belongsTo('App\Role');
}
public function task(){
return $this->hasMany('App\Task');
}
public function share(){
return $this->hasMany('App\Share');
}
}
And this is the house Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\User;
use App\Bill;
class House extends Model {
protected $fillable = [
'id','house_address', 'house_admin', 'nflatmates'
];
public function user(){
return $this->hasMany('App\User');
}
public function bill(){
return $this->hasMany('App\Bill');
}
}
The thing is that you obviously have oneToMany relationship there, so what you want to do is something like this.
public function store(Request $request){
$bill = Bill::create($request->all());
$users = User::where('house_id', Auth::user()->house->id);
$nflatmates = Auth::user()->house->nflatmates;
$shared_amount = $bill->bill_amount / $nflatmates;
foreach($users as $user){
$data = [
'share_amount' => $shared_amount,
'share_status' => 'XXXXXX'
];
$share = new Share($data);
//Associating user with this share
$share->user()->associate($user);
//Associating bill with this share
$share->bill()->associate($bill);
//Saving share
$share->save();
}
return redirect('/admin/bills');
}
EDIT:
In order for the code above to work, you must have a valid relationships set across your models.
EDIT 2:
I thought that nflatmates was a oneToMany relationship, but it isn't so there is no need for attach function.
We are now creating a Share object and through it's relationships that are defined we are using associate function based on Belongs To Relationships which you can find on official docs, just scroll down a bit.

Laravel : One is to Many Relationship, get foreign key value

I'm currently working on productcategories and products relationship
which is one is to many.
I wonder why i'm having this error
Call to undefined method stdClass::products()
Here's my model for Products
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
public $table = "products";
public $fillable = ['productcategory_id',
'name',
'description',
'price',
'pic',
'availability',
'featured',
];
public function productcategory()
{
return $this->belongsTo('App\ProductCategory');
}
}
and here's my model for Product Category
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ProductCategory extends Model
{
public $table = "productcategories";
public $fillable = ['name',
'description',];
public function products(){
return $this->hasMany('App\Product','productcategory_id','id');
}
}
and Here's my view file
#foreach($productcategories as $productcategory)
#foreach($productcategory->products() as $product)
{{ $product->name }}
#endforeach
#endforeach
Please help me i'm getting this error all the time.
Just shooting in the dark here, but I would first remove the parentheses in your view file from $productcategory->products(). If that doesn't solve the issue, dd($productcategories) in your Controller and verify that you're passing a Collection of ProductCategory as I wouldn't expect the resulting error to include reference to "stdClass".

Categories