How to use Sync in ManytoMany and add statut to pivot table - laravel

I have three table and i'm in a many to many relationship.
Table "semaine"
id | name |
Table "bitheme"
id | namebitheme
Table "bitheme_semaine". In it i add a new "statut" columns
bitheme_id | semaine_id | statut
In my case I can have a status of 1 or 2 if the relationship exists (in database it"s 1 by defaut on this field).
In my edit form i have to checkbox :
<input type="checkbox" name="bithemes[]" value="{{ $id }}"> </input>
<input type="checkbox" name="statut[]" value="{{ $id }}"> </input>
I synchronize my table as well :
$semaine->update($request->all());
$semaine->bithemes()->sync($request->input('bithemes', []));
But i also want to synchronize the status if it is checked.
So I added in the relationship of my Semaine model :
public function bithemes()
{
return $this->belongsToMany(Bitheme::class)->withPivot('statut');
}
and i try to test synchronize with :
$semaine->bithemes()->sync($request->input('bithemes', []), ['statut' => 2]);
But the statut is never synchronized.

The sync method takes as first parameter an array of IDs as in your example. But when you want to sync the attributes of the pivot table, give it an array with the IDs as numeric index of that array and an array as value of each index for the pivot attributes.
$syncable = [];
foreach($request->input('bithemes', []) as $bithemeId) {
$syncable[$bithemeId] = ['status' => 2];
}
$semaine->bithemes()->sync($syncable);
For precision, the format you used is to sync only one ID
if (isset($request->input('bithemes')[0])) {
$semaine->bithemes()->sync($request->input('bithemes')[0], ['statut' => 2]);
}

Perfect.
I changed my form with :
<input type="checkbox" name="bithemes[]" value="{{ $id }}" {{ (in_array($id, old('bithemes', [])) || isset($semaine) && $semaine->bithemes->contains($id)) ? 'checked' : '' }}> </input>
<input type="checkbox" name="statut[]" value="{{ $id }}"> </input>
and my controller with :
$syncable = [];
foreach($request->input('bithemes', []) as $key => $value) {
if (!isset ($request->input('statut')[$key])) {$statut = 1;} else {$statut = 2;}
$syncable[$value] = ['statut' => $statut];
}
$semaine->bithemes()->sync($syncable);
and everything si fine now. Thanks

Related

Invalid parameter number error when using whereIn with livewire

I am trying to create a multiple select filter. When I use whereIn for the id I get this error
Invalid parameter number (SQL: select * from stations where id in (16128))
With 16128 being one of the two id's selected for that query. It works fine with a manual given array so I am not sure what the problem is. I also tried with array_values but I get the same result.
<label for="" class="col-md-3">Daypart</label>
<div class="container-checkbox">
#foreach($dayparts as $id => $daypart)
<div wire:key="{{ $daypart->id }}">
<input type="checkbox" id="{{ $daypart->daypart_name }}" name="{{ $daypart->daypart_name }}" wire:model="filters.dayparts.{{ $daypart->id }}" wire:click="filtru({{ $daypart->id }})" />
</div>
#endforeach
</div>
public function filtru($id){
$this->filters['dayparts'] = array_filter($this->filters['dayparts']);
$dayp = DaypartStation::whereIn('daypart_id', array_keys($this->filters['dayparts']))->get();
foreach($dayp as $day){
$this->arr[]=$day->station_id;
}
$toate = Station::whereIn('id', [$this->arr])->get();
//dd(Station::whereIn('id', $this->arr)->get());
//dd(Station::whereIn('id', [array_values($this->arr)])->get());
// dd(Station::whereIn('id', [16576, 16776, 16376])->get());
}
public function render()
{
return view('livewire.create-workbooks-table', [
'stations'=> Station::
when($this->filters['dayparts'], function($query){
$query->whereIn('id', [$this->arr]);
})
->search($this->search)
->orderBy($this->sortBy, $this->sortDirection)
->latest()
->Paginate($this->perPage),
]);
}
I believe you should change
$toate = Station::whereIn('id', [$this->arr])->get();
to
$toate = Station::whereIn('id', $this->arr)->get();
since $this->arr is already an array

Laravel - old() value of checkbox, in combination with the one loaded from the database

I have this checkbox code:
<div>
<input type="checkbox" id="allowfullscreen" name="allowfullscreen"
{{ $gallery->allowfullscreen == 1 ? 'checked' : '' }}>
</div>
This checks the checkbox based on the value taken from the database. Now, i would like to implement also the old data, in case of a failed submission.
For textboxes i do it like this:
<input id="galname"
type="text"
class="form-control #error('galname') is-invalid #enderror"
name="galname"
value="{{ old('galname') ?? $gallery->galname }}"
required autocomplete="galname" autofocus>
But it does not work this way for checkboxes since they need checked to be printed. The samples I found around here on SO only adress one of the two situations, but didnt find any that address both things for checkboxes.
How can this be done?
The second parameter you give the the old() function is used when the first value is null. So when you do old('name', "test") and no old value for 'name' is found, 'test' is used. So in your case, you could use:
<div>
<input type="checkbox" id="allowfullscreen" name="allowfullscreen"
{{ old('allowfullscreen', $gallery->allowfullscreen) == 1 ? 'checked' : '' }}>
</div>
This is ok even if you use all inputs in one blade template to use with create and update actions.
Will only work for POST request, obviously.
#if(old('published', (old('_token') ? false : ($slider->published ?? false)))) checked #endif
<div class="form-group">
<input class="form-check-input" type="checkbox" id="publish" name="published" value="1" #if(old('published', (old('_token') ? false : ($slider->published ?? false)))) checked #endif>
<label class="form-check-label" for="publish">{{ __('PubliƩ') }}</label>
#include('bo.modules.input-error', ['inputName'=>'published'])
</div>
Using this on request
/**
* Prepare the data for validation.
*
* #return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'published' => 'required|boolean',
];
}

Laravel checkbox problem which I cannot update the pivot table

I'm working on a laravel application. In laravel I'm trying to update a row from a pivot table. In this application I have three tables:
issues: id, title, description
categories: id, name
category_issues:(pivot table) id, issue_id, category_id
In laravel I'm trying to update a row from a pivot table. I have this relationships:
Issue.php
public function categories() {
return $this->belongsToMany('App\Category', 'category_issues');
}
Category.php
public function issues() {
return $this->belongsToMany('App\Issue', 'category_issues');
}
A issue can have many categories.
Html code for displaying category section:
<div class="form-group row">
<label for="category" class="col-md-4 col-form-label text-md-right">{{ __('Category :') }}</label>
<div class="form-check col-md-6">
#foreach ($categories as $category)
<input type="checkbox" name="category[]" id="{{ $category->id }}" value="{{ $category->id }}"
{{ in_array($category->id, $issue->categories->pluck('id')->toArray()) ? 'checked' : '' }}>
<label for="{{ $category->id }}"> {{ $category->name }} </label>
#endforeach
</div>
</div>
Here is the update function file:
public function update(Issue $issue)
{
request()->validate([
'title'=> ['required', 'min:3'],
'description'=> ['required', 'min:3'],
'category' => ['required']
]);
$issue->title = request('title');
$issue->description = request('description');
$issue->save();
//Category_Issue Update
$categoryIssue = new CategoryIssue;
$cats = request('category');
foreach ($cats as $cat) {
$categoryIssue->updateOrCreate([
'issue_id'=> $issue->id,
'category_id' => $cat
]);
}
return redirect('issues')->with('success', 'Issue has been updated');
}
Instead of doing the creation / update on the CategoryIssue model (do you even have a CategoryIssue model?), Laravel makes this really easy to update the pivots of a relationship all at once, with the sync() method. You've set up the relationships correctly already per your code above.
In your update() method, you already have your Issue model, and have saved it. You have a set of categories coming in from the form via the $request object (or none if user didn't choose any), so you can then update the pivot like this:
$issue->categories()->sync($request->get('category', []));
Leaving a blank array as the second argument is optional. One small suggestion: maybe make the name of the field on the form 'categories' instead of 'category' just to keep it easy to remember it is an array coming in.

BACKPACK Laravel manage extra columns in pivot table in a many-to-many relationship using Backpack CRUD

I'm using backpack for laravel and I'm trying to add/update some extra columns in a pivot table used in a many-to-many relationship.
Summarizing the context: I have a model Task, another model Machine and this intermediate pivot table machine_task containing the many-to-many relation between Task and Machine
In this machine_task there are machine_id, task_id and then boolean columns m (for 'monthly'), q (for 'quarterly'), b (for 'biannual') and y (for 'yearly').
This is what I have
In my Models/Task.php I've defined the m-2-m relationship
public function machines()
{
return $this->belongsToMany('App\Machine')->withPivot('m','q','b','y');
}
In /app/Http/Controllers/Admin/TaskCrudController.php I have the fields, the most relevant one being this
$this->crud->addField([ // n-n relationship
'label' => "Machines", // Table column heading
'type' => "select2_from_ajax_multiple_custom", // a customized field type modifying the standard select2_from_ajax_multiple type
'name' => 'machines', // the column that contains the ID of that connected entity
'entity' => 'machines', // the method that defines the relationship in your Model
'attribute' => "name", // foreign key attribute that is shown to user
'model' => "App\Models\Machine", // foreign key model
'data_source' => url("api/machines"), // url to controller search function (with /{id} should return model)
'placeholder' => "Select machine(s)",
'minimum_input_length' => 0,
'pivot' => true,
'dependencies' => ['building_id'], // this "Machines" field depends on another previous field value
]);
This works perfectly: When I create or update a Task, the AJAX call is made returning the right results, values are correctly added into the select2 input, and the pivot table machine_task is correctly filled and updated with task_id and machine_id when I click on the Save and back button.
But how to insert into the pivot table the extra values m,q,b,y alongside task_id and machine_id?
At the end of TaskCrudController.php I have
public function store(StoreRequest $request)
{
// your additional operations before save here
// What I should do here to get the pivot values into the request??????????????
$redirect_location = parent::storeCrud($request);
// your additional operations after save here
// use $this->data['entry'] or $this->crud->entry
return $redirect_location;
}
public function update(UpdateRequest $request)
{
// your additional operations before save here
// What I should do here to get the pivot values into the request??????????????
$redirect_location = parent::updateCrud($request);
// your additional operations after save here
// use $this->data['entry'] or $this->crud->entry
return $redirect_location;
}
In my modified version of select2_from_ajax_multiple I have added some rows with checkboxes for each of the options selected. Let's see in a screenshot for better understanding
select2_from_ajax_multiple_custom
In /vendor/backpack/crud/src/resources/views/fields/select2_from_ajax_multiple_custom.blade.php I initialize the values like this, and then I use jquery to update the rows synced with the select2 control, but I don't know how to associate the m,q,b,y checkboxes with each of the select2 selected options and to pass them to the request.
#if ($old_value)
#foreach ($old_value as $item)
#if (!is_object($item))
#php
$item = $connected_entity->find($item);
#endphp
#endif
<div id="div{{ $item->getKey() }}">
<span> {{ $item->getKey() }} {{ $item->{$field['attribute']} }} -- </span>
Monthly <input type="checkbox" id="m{{ $item->getKey() }}" name="m{{ $item->getKey() }}" value="1" #php if ($item->pivot['m'] == "1") echo "checked"; #endphp >
Quarterly <input type="checkbox" id="q{{ $item->getKey() }}" name="q{{ $item->getKey() }}" value="1" #php if ($item->pivot['q'] == "1") echo "checked"; #endphp>
Biannual <input type="checkbox" id="b{{ $item->getKey() }}" name="b{{ $item->getKey() }}" value="1" #php if ($item->pivot['b'] == "1") echo "checked"; #endphp>
Yearly <input type="checkbox" id="y{{ $item->getKey() }}" name="y{{ $item->getKey() }}"value="1" #php if ($item->pivot['y'] == "1") echo "checked"; #endphp> <br/>
#php
#endphp
</div>
#endforeach
#endif
Thank you very much in advance for your time and I hope you can help me! Kinda stuck with this!
I've been able to solve it, so I'll post the solution as it may be useful for others.
What I did,
In my TaskCrudController.php I added the underlying Task model
// add this to the use statements
use App\Models\Task;
then, also in TaskCrudController.php I made this
// Notice: You need to add this to "create" function as well, I'm just writing here the update function to keep it short.
public function update(UpdateRequest $request)
{
$redirect_location = parent::updateCrud($request);
foreach ($request->machines as $machine) {
$set= array('m'=>'0','t'=> '0', 's' => '0', 'a' => '0');
if (isset($request['m'])) in_array ($machine, $request['m']) ? $set['m'] = '1' : $set['m'] = '0';
if (isset($request['t'])) in_array ($machine, $request['t']) ? $set['q'] = '1' : $set['q'] = '0';
if (isset($request['s'])) in_array ($machine, $request['s']) ? $set['b'] = '1' : $set['b'] = '0';
if (isset($request['a'])) in_array ($machine, $request['a']) ? $set['y'] = '1' : $set['y'] = '0';
Task::find($request->id)->machines()->syncWithoutDetaching([$machine => $set]);
return $redirect_location;
}
// Code explanation:
// what we are doing basically here is to grab the $request data
// For example: In the $request we receive m[3] b[1,3] y[1] arrays
// meaning that for our task:
// Machine 1 has no monthly, no quarterly but biannual and yearly checkboxes checked
// Machine 3 has monthly, no quarterly, biannual and no yearly checkboxes checked
// with the loop above, we cast that incoming data into this structure
// $set would contain after the loop:
// '1' => ['m' => '0', 'q'=> '0', 'b' => '1', 'y' => '1']
// '3' => ['m' => '1', 'q'=> '0', 'b' => '1', 'y' => '0']
// with that, we updated the intermediate table using syncWithoutDetaching
Now let's see select2_from_ajax_multiple_custom.blade.php although I'm not posting all code (the rest is the same as select2_from_ajax_multiple standard field)
<div #include('crud::inc.field_wrapper_attributes') >
<label>{!! $field['label'] !!}</label>
#include('crud::inc.field_translatable_icon')
<select
name="{{ $field['name'] }}[]"
style="width: 100%"
id="select2_ajax_multiple_custom_{{ $field['name'] }}"
#include('crud::inc.field_attributes', ['default_class' => 'form-control'])
multiple>
#if ($old_value)
#foreach ($old_value as $item)
#if (!is_object($item))
#php
$item = $connected_entity->find($item);
#endphp
#endif
<option value="{{ $item->getKey() }}" selected>
{{ $item->{$field['attribute']} }}
</option>
#endforeach
#endif
</select>
// What I added is:
<div id="freq">
#if ($old_value)
#foreach ($old_value as $item)
#if (!is_object($item))
#php
$item = $connected_entity->find($item);
#endphp
#endif
<div id="div{{ $item->getKey() }}">
<span>{{ $item->{$field['attribute']} }} -- </span>
Monthly <input type="checkbox" id="m{{ $item->getKey() }}" name="m[]" value="{{ $item->getKey() }}" #php if ($item->pivot['m'] == "1") echo "checked"; #endphp >
Quarterly <input type="checkbox" id="q{{ $item->getKey() }}" name="q[]" value="{{ $item->getKey() }}" #php if ($item->pivot['q'] == "1") echo "checked"; #endphp>
Biannual <input type="checkbox" id="b{{ $item->getKey() }}" name="b[]" value="{{ $item->getKey() }}" #php if ($item->pivot['b'] == "1") echo "checked"; #endphp>
Yearly <input type="checkbox" id="y{{ $item->getKey() }}" name="y[]"value="{{ $item->getKey() }}" #php if ($item->pivot['y'] == "1") echo "checked"; #endphp> <br/>
</div>
#endforeach
#endif
</div>
// js code to add or remove rows containing the checkboxes (This needs to be put inside <script> tags obviously) $("#select2_ajax_multiple_custom_machines").on("select2:select", function(e) {
// add checkbox row
htmlRow = "<div id=\"div"+e.params.data.id+"\">"+"<span> "+e.params.data.text+"-- </span>"+" Monthly <input type=\"checkbox\" id=\"m"+e.params.data.id+"\" name=\"m[]\" value=\""+e.params.data.id+"\">";
htmlRow += " Quarterly <input type=\"checkbox\" id=\"q"+e.params.data.id+"\" name=\"q[]\" value=\""+e.params.data.id+"\">";
htmlRow += " Biannual <input type=\"checkbox\" id=\"b"+e.params.data.id+"\" name=\"b[]\" value=\""+e.params.data.id+"\">";
htmlRow += " Yearly <input type=\"checkbox\" id=\"y"+e.params.data.id+"\" name=\"y[]\" value=\""+e.params.data.id+"\"><br/>";
htmlRow += "</div>";
$("#freq").append(htmlRow);
});
$("#select2_ajax_multiple_custom_machines").on("select2:unselect", function(e) {
// remove checkbox row
$("#div"+e.params.data.id).remove();
});
And that's all, folks.
Hope it helps.

Laravel: needing help querying two tables into one result

Im interested in giving users the option to add articles to custom lists that they create. (like youtube's playlist feature).
I want to know how to show a 'checked' checkbox next to a list if the article already exists within it, so they don't add it twice.
My database tables:
list : | id | person_id | list_name | list_desc
list_ref : | id | list_id | article_id
My code for displaying lists.
$lists = Lists::where('person_id', Auth::user()->person_id)->get();
#foreach($lists as $list)
<input type="checkbox" id="available_list" value="{{ $list->id }}">
{{ $list->list_name }}
#endforeach
If anyone could help me understand the code to query the two tables to see if list already used || list is available.
You could use a raw expression in conjunction with a left join like this:
$lists = DB::table('list')->
leftJoin('list_ref', 'list_ref.list_id', '=', 'list.id')->
select(DB::raw('list.*, IF(list_ref.list_id = list.id, 1, 0) used'))->
where('person_id', Auth::user()->person_id)->
get();
The above uses a join to figure out which list rows has a corresponding key in the list_ref table. The result will contain a field called used that will tell if the entry has a reference in the list_ref table.
You should now be able to generate you form like this:
#foreach($lists as $list)
<input type="checkbox" id="available_list" value="{{ $list->id }}" {{ $list->used ? "CHECKED" : "" }}>
{{ $list->list_name }}
#endforeach
First crate class, here is example for facility
class HotelFacilities
{
public $facility_id;
public $facility;
public $status;
}
Get all data
$facilities = Facility::all();
Get user related data
$user_facilities = UserFacility::where('user_id', 3)->get();
//Here you add status
$array_hotel_facility = array();
for($i=0; $i<count($facilities); $i++)
{
$obj_hotel_facilities = new HotelFacilities;
$obj_hotel_facilities->facility_id = $facilities[$i]['id'];
$obj_hotel_facilities->facility = $facilities[$i]['facility'];
$obj_hotel_facilities->status = false;
for($j=0; $j<count($user_facilities); $j++)
{
if($user_facilities[$j]->facility_id == $facilities[$i]->id)
{
$obj_hotel_facilities->status = true;
}
}
//Pass this array to view
$array_hotel_facility[$i] = $obj_hotel_facilities;
}
//Pass to view
return view('extranet.view_facilities')->with('facilities', $array_hotel_facility);
//Generate UI like this
#foreach($facilities as $facility)
<div class="form-group col-md-3">
<div class="form-group">
<label>
<input type="checkbox" class="minimal" name="facilities[]" value="{{ $facility->facility_id }}" <?php if($facility->status){echo "checked";} ?>>
{{ $facility->facility }}
</label>
</div>
</div>
#endforeach

Resources