I have 3 tables, people, instructors, and trainees. instructor and trainees inherit from people. both three table have relation to fee_instructor table. here is all the models.
// Person.php
class Person extends \Eloquent {
// Add your validation rules here
public static $rules = [
// 'title' => 'required'
'name'=>'required',
'institution_id'=>'required',
'pob'=>'required',
'dob'=>'required'
];
// Don't forget to fill this array
protected $fillable = [
'name',
'title',
'position',
'institution_id',
'pob',
'dob',
'photo',
'last_education',
'nip',
'role_id'
];
public function fee(){
return $this->belongsToMany('Fee', 'fee_instructor', 'person_id');
}
}
// Instructor.php
class Instructor extends \Eloquent {
// Add your validation rules here
public static $rules = [
// 'title' => 'required'
];
// Don't forget to fill this array
protected $fillable = [
'name',
'title',
'position',
'institution_id',
'pob',
'dob',
'photo',
'email',
'last_education',
'nip'
];
public function fee(){
return $this->belongsToMany('Fee', 'fee_instructor', 'person_id');
}
}
// Trainee.php
class Trainee extends \Eloquent {
// Add your validation rules here
public static $rules = [
// 'title' => 'required'
];
// Don't forget to fill this array
protected $fillable = [
'name',
'title',
'position',
'institution_id',
'pob',
'dob',
'photo',
'last_education',
'nip',
'reg_date',
'company_name',
'marital_status',
'email'
];
public function fee(){
return $this->belongsToMany('Fee', 'fee_instructor', 'person_id');
}
}
// Fee.php
class Fee extends \Eloquent {
// Add your validation rules here
public static $rules = [
// 'title' => 'required'
];
// Don't forget to fill this array
protected $fillable = ['name', 'tarif', 'unit_id'];
public function unit(){
return $this->belongsTo('Unit');
}
public function instructor(){
return $this->belongsToMany('Instructor', 'fee_instructor');
}
public function person(){
return $this->belongsToMany('Person', 'fee_instructor');
}
public function trainee(){
return $this->belongsToMany('Trainee', 'fee_instructor');
}
}
when i try to update an entry that is not belongs to instructor (in both person or trainee), laravel throw the following error.
SQLSTATE[23503]: Foreign key violation: 7 ERROR: insert or update on table "fee_instructor" violates foreign key constraint "fee_instructor_instructor_id_foreign" DETAIL: Key (person_id)=(5) is not present in table "instructors".
how is the correct way to use the pivot table for these scenario?
Ah so your using postgresql. That' why I thought it was weird that you were talking about table inheritance. Take note that based on the docs http://www.postgresql.org/docs/9.0/static/ddl-inherit.html
Inheritance does not automatically propagate data from INSERT or COPY commands to other tables in the inheritance hierarchy.
INSERT always inserts into exactly the table specified.
...
In some cases it is possible to redirect the insertion using a rule (see Chapter 37).
So if you're not in the bounds of the rule, you may be incorrectly assuming that a row that exists in instructor also exists in the super-table person and vice-versa. You're update/insert on fee_instructor is failing simply because a row with person_id = 5 does not exist in the instructor table. It may exist in the person table but that is irrelevant. If it does not exist in instructor it wont work.
If you scroll through the docs about inheritance, you will also come up with the caveats of using inheritance:
A serious limitation of the inheritance feature is that indexes (including unique constraints)
and foreign key constraints only apply to single tables, not to their inheritance children.
So if you really want FK relationships with fee_instructor for both instructor and trainee then separate FKs have to exist for each table which I'm assuming you already have in place.
Please also take not that eloquent's regular relationship does not work well with the idea of inheritance. However, you may be able to achieve an inheritance type of behavior using polymorphic relations: http://laravel.com/docs/4.2/eloquent#polymorphic-relations
Related
I have created a many to many relationship between two tables with a third pivot table.
The thing that makes the situation a little difficult is I am linking the Apps table based on name and not ID. It is because I update the App list from a third party and app name will always be consistent, where ID can possibly change if App is removed at some point, and then re-added, etc.
Apps
id
name // This is the name of the app, it will never change for a particular app and is short, all lowercase, no spaces, and unique
label // This is the user friendly name
Plans
id
name
etc
apps_plans pivot table
id
apps_name
plans_id
I've finally got everything working perfectly in Laravel itself, but I cannot figure out at all how to get this to work correctly in Backpack for my Admin portal. I've gotten it to the point where everything works perfect until I try to update or create a new plan. The Apps I select using the select2 type, it tries to insert them into the pivot table with an ID number and not with the name.
Randomizing some names, my mistake if things don't match perfectly. This aspect works fine from all tests I've done:
Plans Model:
{
use CrudTrait;
protected $table = 'plans';
protected $guarded = ['id'];
public function apps()
{
return $this->belongsToMany('App\Apps', 'apps_plans', 'plans_id', 'apps_name', 'id', 'name');
}
}
Apps Model:
class Apps extends Model
{
use CrudTrait;
protected $table = 'apps';
protected $guarded = ['id'];
protected $casts = [
'json' => 'array',
];
public function plans()
{
return $this->belongsToMany('App\Plan', 'apps_plans', 'apps_name', 'plans_id', 'name', 'id');
}
}
**Note I removed the fillable variable , I didn't want to expose all variables in my columns.
Backpack Plans CrudController:
public function setup()
{
CRUD::setModel(\App\Plan::class);
CRUD::setRoute(config('backpack.base.route_prefix') . '/plan');
CRUD::setEntityNameStrings('plan', 'plans');
$this->crud->addColumn([
'name' => 'apps',
'type' => 'relationship',
'label' => 'Apps',
'entity' => 'apps',
'attribute' => 'label',
'model' => \App\Apps::class,
]);
}
protected function setupCreateOperation()
{
CRUD::setValidation(PlanRequest::class);
CRUD::setFromDb(); // fields
$this->crud->addField('apps', [
'name' => 'apps',
'type' => 'select2_multiple',
'entity' => 'apps',
'attribute' => 'label',
'label' => 'Apps',
'pivot' => true,
]);
I removed quite a bit to keep my project details private, I hope it makes sense. I think all important details are still in. Anyone know if this is an issue with Backpack? Or did I miss an option somewhere, where you can set which column it uses for the relationship. It is clearly not taking it from the model because the models work just as intended on their own...
Thanks!
Edit: here is my migration I am using, it works flawlessly--even in phpmyadmin it gives me a drop down of items to select from
{
Schema::create('apps_plans', function (Blueprint $table) {
$table->id();
$table->string('apps_name');
$table->foreign('apps_name')->references('name')->on('apps');
$table->unsignedBigInteger('plans_id');
$table->foreign('plans_id')->references('id')->on('plans');
});
}
EDIT 2:
This is the error I am getting when trying to do a Create or Update:
{
"error": "There is a problem with your request",
"message": "SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`api`.`apps_plans`, CONSTRAINT `apps_plans_apps_name_foreign` FOREIGN KEY (`apps_name`) REFERENCES `apps` (`name`)) (SQL: insert into `apps_plans` (`apps_name`, `plans_id`) values (2, 4))"
}
Again I removed some details that were very specific to my project but I don't think I changed any of the logic in the error message. You can see everything looks great about the query except that at the very end, it is inserting the App ID instead of the App name as it should be.
I suspect that the current configuration will have the same result in Laravel directly. ie running something like $plan = Plan::find($somePlanId); $app = App::find($someAppId); $plan->apps()->attach($app); would result in the same error.
Since name is the key that matters for the apps table, consider dropping the autoincrementing id for that table and instead setting
In the migration for the apps table, do:
$table->string('name')->primary();
Then in your apps model, do:
protected $primaryKey = 'name';
public $incrementing = false;
protected $keyType = 'string';
Now, Laravel (and by proxy Backpack) should treat the relationship the way you expect.
Given my Models Person and Student. Now, one of my controller is doing this:
$person_id = \App\Models\Person::insertGetId(['name' => $request->name, ...]);
\App\Models\Student::create(['person_id' => $person_id, ...]);
$student = \App\Models\Student::where('person_id', $person_id)->first();
dd($student);
During execution, everything has been save but My problem is why i am receiving null on die and dump?
By the way, my student tables' primary and foreign key is the person_id column.
Edit
class Student extends Model
{
protected $fillable = ['section', 'current_year'];//there are other attribute here which I didn't show because I am using mobile
public function person(){
return $this->belongsTo('App\Models\Person', 'person_id', 'id');
}
...
}
Add person_id in $fillable property:
protected $fillable = ['section', 'current_year', 'person_id'];
I have an Item model, that has ItemTranslations, I'm receive a Request with the Item I'm updating and the translations for that Item.
Now some of those translations will already be in the database (they have their own id), and some of them will be new. Is there a clean way to go about this?
In pseudo: Update the Item with this request, for each Translations you find in this request, update the translation relation if it exists, else create it for this Item.
This is the layout of my Item.
class Item extends Model
{
protected $fillable = ['menu_id', 'parent_id', 'title', 'order', 'resource_link', 'html_class', 'is_blank'];
public function translations()
{
return $this->hasMany(MenuItemTranslation::class, 'menu_item_id');
}
}
This is the ItemTranslation
class ItemTranslation extends Model
{
protected $table = 'item_translations';
protected $fillable = ['menu_item_id', 'locale', 'name', 'order', 'description', 'link', 'is_online'];
}
You could use the updateOrCreate method on the translations() relationship. For example:
$item->translations()->updateOrCreate([
'name' => $translation['name'],
// Fields that should be used to find an existing record.
], [
'description' => "New description",
// Fields that should be updated.
]);
The first argument is the data that you want Eloquent to look up the translation by and second argument is the data that you want to be updated.
Both arguments will be used to create a new translation when an existing one couldn't be found.
So i have relationship declared on the parents Model as
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Parents extends Model
{
protected $fillable = [
'firstName',
'middleName',
'lastName',
'phoneNumber',
'gender',
];
public function students(){
return $this->belongsToMany('App\Student','parent_student','parentId','studentId');
}
}
with the anchor table parent_student and a students table with the column classId. here is the students model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
//
protected $fillable = [
'firstName',
'middleName',
'lastName',
'regNo',
'gender',
'dob',
'classId',
];
public function parents(){
return $this->belongsToMany('App\Parents','parent_student','studentId','parentId');
}
public function classes()
{
return $this->belongsTo('App\Classes','classId');
}
}
what i am tring to achieve is to get the email addresses for parents with students in one class eg class 1, which i pass the classId from a dropdown select in a form on the view to the controller.
Here is my controller.
public function submitEmail(Request $request)
{
$validatedData = $request->validate([
'class' => 'required|integer',
'message' => 'string|max:255',
'subject' => 'string|max:255',
]);
$class = $request->input('class');
$message = $request->input('message');
$subject = $request->input('subject');
$parents = Parents::all();
$parents = Parents::where($parents->students->classId,"=",$class)->get();
die($parents);
}
I am fairly new to laravel and this is the far i have gone so far.Any suggestions would be much appreciated.(I am using laravel 5.6)
This line
$parents->students
is where your problem is. $parents is a Collection (array) of Parents models, not a single Parents1 model. To get a single classId, you need to use a Loop:
foreach($parents AS $parent){
... // Should be able to access $parent->students without issue;
}
Next, you're trying to access ->classId of $parent->students, which is the same issue. $parent->students is a Collection, and not a single Student2 model. You'd need another loop to get the classId:
foreach($parents AS $parent){
foreach($parent->students AS $student){
... // Should be able to access `$student->classId` without issue
}
}
But, this still doesn't solve your core issue. It's important to know when you're accessing a Collection vs a single Model, so keep that in mind.
All of that aside, it sounds like you're trying to get all the Parents of Students in Class of $class (passed from your <form>). To accomplish this, you can use ->whereHas(), as below:
$parents = Parents::whereHas("students", function($query) use($class){
$query->where("classId", "=", $class);
})->with(["students" => function($query) use($class){
$query->where("classId", "=", $class);
}])->get();
What this does, is queries the parents table for any student records that have a class with an id of $class.
Note that whereHas and with contains the same subquery to constrain and eager-load each Parents collection of Students to what's required.
Sidenote: Your classes() relationship on Student has an issue. Since it's a belongsTo() method, it should be class() (singular), as $student->classes would only return a single Class model.
1 Pay attention to naming conventions. Parents should be Parent; Model names are singular.
2 You named the Student Model right, so be consistent.
Try this query:
$parents = Parents::with(['students' => function($query) use ($class){
$query->where('classId',$class);
}])->get();
Instead of:
$parents = Parents::where($parents->students->classId,"=",$class)->get();
Then instead of die($parents) use dd($parents)
I try to make a unique validation the settings of my website but this doesn't work :
In my controller :
$rules = array(
'username' => 'required|unique:User,username,10',
'email' => 'required|email|unique:User,email,10',
'language' => 'required|in:fr,en',
);
My model:
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
protected $primaryKey = 'id_user';
protected $table = 'user';
}
The problem is:
My Validator Validator::make(Input::all(), $rules, $messages); fails, it says that this username and email already exist.
Disagree with the answer to your own question:
"Laravel is not done to search in a custom column".
This is not true.
To be precise: There is nothing bad in using a Plugin...
See the important part of a migration file (app/database/migrations):
// creates a DB-table named 'users'
Schema::create('users', function (Blueprint $t) {
$t->increments('id');
$t->timestamps();
// ... Here a unique field
$t->string('user_email_one', 255)->unique();
// ...
});
And the relevant validation rules in the UserController:
$rules = array(
'user_email_one' => 'required|email|unique:users',
// ...
);
And Laravel is doing its job.
With unique: you have to call the DB-table name, not the model name.
BTW: the plugin you've chosen does this...
The Laravel docs about validation:
unique:table,column,except,idColumn
The field under validation must be unique on a given database table.
If the column option is not specified, the field name will be used.
Just as an interesting info about naming a mySQL table 'User', 'user' or 'users', which could have caused your error. Visit this question:
Is there a naming convention for MySQL? asked by StackOverflowNewbie, answered by Tom Mac (highest vote & accepted answer)
Laravel is not done to search in a custom column.
So I have installed this plugin https://github.com/felixkiss/uniquewith-validator who did the job very well:
$rules = array(
'username' => 'required|alpha_dash|unique_with:user,username,10 = id_user',
'email' => 'required|email|unique_with:user,email,10 = id_user',
'language' => 'required|in:fr,en',
);