Laravel Model binding many to many realtionship does not populate data - php

I have two tables with a many to many relation (Project and Center, the pivot table is ProjectCenter).
These are my models:
Project:
class Project extends Model {
public function centers()
{
return $this->belongsToMany('App\Models\Center', 'ProjectCenter', 'IDProject', 'IDCenter');
}
public function getCenterListAttribute()
{
return $this->centers->lists('IDCenter')->all();
}
}
Center:
class Center extends Model {
public function projects()
{
return $this->belongsToMany('App\Models\Project', 'ProjectCenter', 'IDCenter', 'IDProject');
}
}
Controller -> edit:
public function edit($id)
{
$project = Project::find($id);
$centerList = Center::lists('Name', 'IDCenter')->toArray();
return view('project/add', array('centerList' => $centerList))->with('project', $project);
}
And the view:
{!! Form::label('centers_list', 'Center*') !!}
{!! Form::select('centers_list[]',
$centerList,
null,
array(
'class' => 'form-control ',
'required' => 'required',
'multiple' => true,
'data-placeholder' =>
'Select a center'
)
) !!}
But I can not select the data already stored previously.
For example: the project 8 (IDProject) has two centers (1 and 2) but the data is not populated in the multiple select:
What am I doing wrong?

You all time get same result $centerList = Center::lists('Name', 'IDCenter')->toArray(); but you must be get product centers using query with model.
$project = Project::with("centers:Name,IDCenter")->find($id);
$centerList = $project->centers->pluck('Name', 'IDCenter')->toArray();

I already solve the problem using a foreach to select the related centers:
<select multiple="multiple" name="centers[]" id="centers" class="form-control select2" required="required" data-placeholder="Select a center">
#if($centerList)
#foreach($centerList as $key => $center)
<option value="{{$key}}" {{ (collect($selectedCenters)->contains($key)) ? "selected='selected'" : '' }} >{{$center}}</option>
#endforeach
#endif
</select>

Related

Laravel 9: how to use enums in blade (<select>)?

I read this awesome article before asking here.
I created an enum class CaseSeverity to use across all forms that needs it. I couldn't find a way to use the enum within the blade, specifically listing the cases within <select> options.
I did a workaround by mkaing a static function in the enum class:
public static function option()
{
return [
'high' => 'high',
'medium' => 'medium',
'low' => 'low'
];
}
Then made a helper function to fetch the array:
if(!function_exists('case_severity')) {
// Get case severity enum
function case_severity() {
return CaseSeverity::option();
}
}
Then used that in the blade:
<select class="form-select #error('severity') is-invalid #enderror"
name="severity" required
data-control="select2"
data-placeholder="Select"
data-hide-search="true">
<option></option>
#foreach (case_severity() as $item)
<option value="{{ $item }}"> {{ $item }} </option>
#endforeach
</select>
Yes, it working as I want it to be, but I have a strong feeling that there is a better way of doing it? Because to me the code/enum cases are repeated.
Entire enum class:
enum CaseSeverity : string
{
case High = 'high';
case Medium = 'medium';
case Low = 'low';
// Coloring the cases to use in datatables as badges
public function color(): string
{
return match($this)
{
self::High => 'badge-danger',
self::Medium => 'badge-warning',
self::Low => 'badge-success',
};
}
// To use in <select> fields
public static function option()
{
return [
'high' => 'high',
'medium' => 'medium',
'low' => 'low'
];
}
}
You could do it like this if you like it bettter.
In your App\Enums\Severity file:
public static function values(): array
{
return array_column(self::cases(), 'name', 'value');
}
Then you can loop through the values:
#foreach(App\Enums\Severity::values() as $key=>$value)
<option value="{{ $key }}">{{ $value }}</option>
#endforeach

Laravel 8.x - Form checkboxes to pivot table

I am building a platform where students can apply for academic courses.
The students use a form to provide some personal information and then choose at least one course to apply for.
When the form is sent, the ApplicationsController validates the inputs and checks that there is at least one course checked. The controller:
public function student_store(Request $request, Course $course)
{
$request->validate([
'fname' => 'required|string|min:2|max:40',
'lname' => 'required|string|min:2|max:40',
'email' => 'required|email|min:6|max:254',
'course-check' => 'required',
'phone' => 'required|digits:10',
'created_at' => \Carbon\Carbon::now(),
'updated_at' => \Carbon\Carbon::now(),
]);
If the validation is ok, a new Applicant is created in the database using the form inputs:
$input = $request->all();
Applicant::create($input);
However the goal is to also create one or more new instances in the pivot table applicant_course, depending on which one or more courses the student has selected. So far I have this:
$applicant = Applicant::where('fname', $input['fname'] )
->where('lname', $input['lname'] )
->where('email', $input['email'] )
->latest()->first();
$checkboxes = $request->all('course-check');
foreach ($checkboxes as $checkbox){
$applicant->courses()->where('course_id', $checkbox)->attach($course->id);
}
}
However, all that the controller function does, is to validate the inputs and create a new Applicant in the database with their data but nothing is added in the pivot table.
Here is the view structure (ignore the $program variables):
#foreach ($courses as $course)
#if ($course->program_id == $program->id)
<div class="course course-{{$program->id}}-{{$course->id}}">
<div class="course-header">
<label class="course-number" for="course-check-{{$program->id}}-{{$course->id}}">{{$program->id}}0{{$course->id}}0</label>
<label class="course-title" for="course-check-{{$program->id}}-{{$course->id}}">{{$course->title}}</label>
<input type="checkbox" name="course-check[{{ $course->id }}]" class="course-check" id="course-check-{{$program->id}}-{{$course->id}}" value="" >
</div>
</div>
#endif
#endforeach
Here is the Applicant model:
class Applicant extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'fname',
'lname',
'email',
'phone',
];
public function courses()
{
return $this->belongsToMany(Course::class)->withTimestamps();
}
}
Can someone help ?
Thanks in advance!
I think you need to change checkbox name like below
<input type="checkbox" name="course-check[]" class="course-check" id="course-check-{{$program->id}}-{{$course->id}}" value="{{ $course->id }}" >
Then in controller
$checkboxes = $request->get('course-check');
$applicant->courses()->sync($checkboxes);
or
$checkboxes = $request->get('course-check');
$applicant->courses()->attach($checkboxes);
To know difference between attach and sync
Ref:https://laravel.com/docs/8.x/eloquent-relationships#updating-many-to-many-relationships

laravel get value from array of controllers

this is my PermissionController.php
foreach (Route::getRoutes() as $route)
{
$action = $route->getAction();
if (array_key_exists('controller', $action))
{
$controllers[] = $action['controller'];
}
}
return view('permission.create')->withControllers($controllers);
and my permission/create/php is like this :
{!! Form::select('class_name[]', $controllers, null, ['class' => 'form-control', 'multiple']) !!}
here in controller, it put the name of all controllers in $controller and send it to the blade but when i select some of them, the request gives me their keys instead of value
public function store(Request $request)
{
return $request->get('class_name');
}
if I select the first one and third one the output is for example :{"1","3"}
but I want their value like this : {"UserController.php", "TestController.php"}
use this code instead Forms::Select or create your custom List with Laravel built-in List Function :
<select name="class_name[]" multiple>
#foreach($controllers as $class)
<option value="{{$class}}" >{{$class}}</option>
#endforeach
</select>
or simply replace the
$controllers[] = $action['controller'];
with
$controllers[$action['controller']] = $action['controller'];

SQLSTATE[23000] Integrity constraint violation: 1452 in a many-to-many relationship in laravel 5.3

I've got a BlogPost Controller which has a user form that takes inputs from the user through the create() method. Then that form redirects to the store method on the same controller. The problem I'm facing is with the many-to-many relationship. BlogPost model has a many-to-many relationship with the BlogTag model. I'm taking input from a user through checkboxes. The user can choose multiple radio buttons associated with individual tags (form code has been posted below). Everything seems to work fine, but when I'm trying to attach all the tag ids to the post instance in my store() method. I get the error that is shown below. I've looked through all the available solutions on the internet but nothing seems to work for my case. The store method has also been posted below.
QueryException in Connection.php line 761: SQLSTATE[23000]: Integrity
constraint violation: 1452 Cannot add or update a child row: a foreign
key constraint fails (erp_system_solution.posts_tags, CONSTRAINT
posts_tags_ref_tag_id_foreign FOREIGN KEY (ref_tag_id) REFERENCES
blog_tags (tag_id) ON DELETE CASCADE ON UPDATE CASCADE) (SQL:
insert into posts_tags (ref_post_id, ref_tag_id) values (2, 34),
(7, 34), (8, 34), (15, 34))
Form for posting a BlogPost
{{Form::open(array(
'action' => 'Blog\BlogPostController#store',
'class' => 'form-horizontal',
'method' => 'POST',
))}}
{{ Form::hidden('user_id', Auth::user()->user_id) }}
<div class="form-group">
{{ Form::label("post_title", "Post Title", array("class" => "col-sm-2 control-label")) }}
{{ Form::text ("post_title", "", array("class" => "col-sm-6")) }}
</div>
<div class="form-group">
{{ Form::label("post_body", "Post Body", array("class" => "col-sm-2 control-label")) }}
{{ Form::textarea("post_body", "", array("class" => "col-sm-6 col-sm-offset-2 top-info-panels", "rows" => "16", "columns" => "2")) }}
</div>
<div class="form-group">
{{ Form::label("published", "Publish Blog Post", array("class" => "col-sm-2 control-label")) }}
{{ Form::radio("published", "Yes", true) }} Yes
{{ Form::radio("published", "No") }} No
</div>
<div class="form-group">
{{ Form::label("category_id", "Choose a Category", array("class" => "col-sm-2 control-label")) }}
{{ Form::select("category_id", $categories, '-----Choose Any One-----') }}
</div>
<div class="form-group">
{{ Form::label("tag_id", "Choose Appropriate Tags", array("class" => "col-sm-2 control-label")) }}
<div class="col-sm-offset-2">
#for($i=1; $i<count($tags)+1; $i++)
{{ Form::checkbox("tag_id[]", $i), array("class" => "col-sm-offset-2 col-sm-10") }} {{ $tags[$i] }}
#endfor
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
{{Form::submit('Create Post', array("class" => "btn btn-warning"))}}
</div>
</div>
{{Form::close()}}
BlogPostModel
<?php
namespace App;
use App\PostsTagsPivot as Pivot;
use App\BlogCategory;
use App\BlogTag;
use App\Comment;
use App\RegisteredUser;
use Illuminate\Database\Eloquent\Model;
class BlogPost extends Model
{
public $timestamps = true;
protected $table = 'blog_posts';
protected $primaryKey = 'post_id';
protected $fillable = ['post_title', 'post_body', 'published', 'registered'];
public function post_category() {
return $this->belongsTo(BlogCategory::class, 'category_id');
}
public function posts_with_tags() {
return $this->belongsToMany(BlogTag::class, 'posts_tags', 'ref_tag_id', 'ref_post_id');
}
public function comments() {
return $this->hasMany(Comment::class, 'comment_id');
}
public function posts_author() {
return $this->belongsTo(RegisteredUser::class, 'user_id');
}
}
BlogPostController
public function store(Request $r) {
$u_id = $this->user::find((int)$r->input('user_id'));
$cat_id = $this->category::find((int)$r->input('category_id'));
$tag_id = $r->input('tag_id');
$role = $this->user::find((int)$r->input('user_id'))->has_role($u_id);
$registered = ($role == 'super_admin') ? true : false;
$published = ($r->input('published') == 'Yes') ? true : false;
$create_post = $this->blog_posts::create(['post_title' => $r->input('post_title'), 'post_body' => $r->input('post_body'), 'published' => $published, 'registered' => $registered]);
$create_post->save();
$create_post->posts_with_tags()->attach($tag_id);
$create_post->save();
$create_post->posts_author()->associate($u_id);
$create_post->save();
$create_post->post_category()->associate($cat_id);
if($create_post->save()) return redirect()->route('blog_post.create')->with('post_saved', 'Your post has been saved');
else return redirect()->route('blog_post.create')->with('post_not_saved', 'Something went wrong');
}
BlogTagModel
<?php
namespace App;
use App\BlogPost;
use Illuminate\Database\Eloquent\Model;
class BlogTag extends Model
{
public $timestamps = true;
protected $table = 'blog_tags';
protected $primaryKey = 'tag_id';
protected $fillable = ['tag_name', 'post_id', 'tag_id'];
public function tags_on_posts() {
return $this->belongsToMany(BlogPost::class, 'posts_tags', 'ref_tag_id', 'ref_post_id');
}
}
Migration for a pivot table joining BlogPost with BlogTag
public function up()
{
Schema::create('posts_tags', function ($table) {
$table->integer('ref_tag_id')->unsigned()->index();
$table->foreign('ref_tag_id')->references('tag_id')->on('blog_tags')->onDelete('cascade')->onUpdate('cascade');
$table->integer('ref_post_id')->unsigned()->index();
$table->foreign('ref_post_id')->references('post_id')->on('blog_posts')->onDelete('cascade')->onUpdate('cascade');
});
}
Well, I figured it out. The solution is to change the ordering in posts_with_tags() action/method
It was:
public function posts_with_tags() {
return $this->belongsToMany(BlogTag::class, 'posts_tags', 'ref_tag_id', 'ref_post_id');
}
It should be:
public function posts_with_tags() {
return $this->belongsToMany(BlogTag::class, 'posts_tags', 'ref_post_id', 'ref_tag_id');
}

Laravel update makes new table rule instead of updating

I have a company table and an attributes table with all sorts of value in it.
One company hasMany attributes and an attribute belongsTo a company.
Now I have a value inside the attributes table with a 'account_nr_start' (for example, when a new user is added to a company its account_id starts counting up from 1000).
Controller:
public function __construct(Company $company, User $user)
{
if(Auth::user()->usertype_id == 7)
{
$this->company = $company;
}
else
{
$this->company_id = Auth::user()->company_id;
$this->company = $company->Where(function($query)
{
$query->where('id', '=', $this->company_id )
->orWhere('parent_id','=', $this->company_id);
}) ;
}
$this->user = $user;
$this->middleware('auth');
}
public function edit(Company $company, CompaniesController $companies)
{
$companies = $companies->getCompaniesName(Auth::user()->company_id);
$attributes = $company->attributes('company')
->where('attribute', '=', 'account_nr_start')
->get();
foreach ($attributes as $k => $v) {
$nr_start[] = $v->value;
}
return view('company.edit', ['company' => $company, 'id' => 'edit', 'companies' => $companies, 'nr_start' => $nr_start]);
}
public function update(UpdateCompanyRequest $request, $company, Attribute $attributes)
{
$company->fill($request->input())->save();
$attributes->fill($request->only('company_id', 'attribute_nr', 'value'))->save();
return redirect('company');
}
HTML/Blade:
<div class="form-group {{ $errors->has('_nr_') ? 'has-error' : '' }}">
{!! HTML::decode (Form::label('account_nr_start', trans('common.account_nr_start').'<span class="asterisk"> *</span>', ['class' => 'form-label col-sm-3 control-label text-capitalize'])) !!}
<div class="col-sm-6">
{!! Form::text('value', $nr_start[0], ["class"=>"form-control text-uppercase"]) !!}
{!! $errors->first('account_nr_start', '<span class="help-block">:message</span>') !!}
</div>
</div>
When I update a company now, it will upload like the last input here: :
So it makes a new rule, while it needs to edit the current attribute rule instead of making a new rule with an empty company_id/attribute.
If I understand what you are trying to do, I think this will fix your problem. The issue you have is the Attribute model is a new instance of the model rather than retrieving the model you need.
before running fill() from the attributes method try this
$new_attribute = $attributes->where('company_id', '=', $company->id)->where('attribute', '=', 'account_nr_start')->first();
Then run the fill()
$new_attribute->fill($request->only('company_id', 'attribute_nr', 'value'))->save();

Categories