Laravel 8 many to many relationshiop update/delete API (pivot table) - laravel

Hello I need a help with laravel 8 many to many relationship pivot table, when I updating or deleting data in API.
These are my files info.
Firstly I created two tables:
Companies (migration file)
public function up()
{
Schema::create('companies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}
Contacts (migration file)
public function up()
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}
After this created pivot table company_contact (migration file)
public function up()
{
Schema::create('company_contact', function (Blueprint $table) {
$table->foreignId('company_id')->constrained();
$table->foreignId('contact_id')->constrained();
});
}
Models looks like this:
Company model
class Company extends Model{
use HasFactory;
protected $fillable = [
'name',
'email'
];
public function contacts()
{
return $this->belongsToMany(Contact::class);
}
}
Contact model
class Company extends Model{
use HasFactory;
protected $fillable = [
'name',
'email'
];
public function contacts()
{
return $this->belongsToMany(Contact::class);
}
}
My routes file
Route::apiResource('companies', CompanyController::class);
My company controller
class CompanyController extends Controller{
public function index()
{
return CompanyResource::collection(Company::all());
}
public function store(CompanyStoreRequest $request)
{
$company = Company::create($request->validated());
$company->contacts()->attach($request->input('contact_id'));
return new CompanyResource($company);
}
public function show(Company $company)
{
//
}
public function update(CompanyStoreRequest $request,Company $company)
{
$contacts = Company::where('id', $company->id)->contacts()->get();
$company->contacts()->sync($request->input(['contact_id']));
$company->update($request->validated());
return new CompanyResource($company);
}
public function destroy(Company $company)
{
}
}
Company store request
public function rules()
{
return [
'name' => ['required'],
'email' => ['required', 'email'],
'contact_id' =>['required']
];
}
company resource
public function toArray($request)
{
//return parent::toArray($request);
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'contact_count' => $this->contacts()->count()
];
}
Maybe somebody can help my to write correct store() and destroy() methods in to company controller. For example when I updating company data also I want to add more contacts and to save these contacts id's in the pivot table.
With destroy method, when I delete certain company, also I want automatically delete these id’s in pivot table (relation company with contacts).
Thanks for any help.

First, you can pass the data of the contacts in an array together with the company's, using an array for several contacts and to record with the relationship use a foreach with the attach method, search on it. About the deletion you can use the softDelete, search on it too, it makes the cascade deletion of the items.

Related

Save OneToOne relation in laravel

I have this model :
class User extends Authenticatable
{
protected $fillable = [
'email',
'password',
'role_id',
];
.........
public function language() {
return $this->hasOne(Language::class, 'language_id');
}
}
And Language model :
class Language extends Model
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'iso'
];
/**
* #return BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
Now When I try to save user :
$newUser->language()->save(Language::find($input['language']));
I get the error:
Column not found: 1054 Unknown column 'language_id' in 'field list' (SQL: update `languages` set `language_id` = 4, `languages`.`updated_at` = 2021-08-02 13:51:32 where `id` = 1)"
User migration :
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->integer('role_id')->default(UserRoles::TRANSLATOR);
$table->integer('language_id')->references('id')->on('languages')->nullable();
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
});
}
Language migration :
public function up()
{
Schema::create('languages', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('name');
$table->string('iso')->unique();
});
}
I confirm that I have language id inside users table and I don't have user_id inside language table, because I want to keep just language_id inside table. What can I try next?
When you have hasOne in User model:
public function language() {
return $this->hasOne(Language::class, 'language_id');
}
eloquent assumes that languages table would have language_id. Hence the error 'no language_id in languages table'
So, hasOne should be defined in the the Language model.
//Language model
public function user() {
return $this->hasOne(User::class); //this assumes users table would have language_id
}
And, the belongsTo should be defined in User model.
//User model
public function language() {
return $this->belongsTo(Language::class);
}
as the first answer, change the sintax of your relationship and add the language_id to the fillable array
user model
protected $fillable = [
'email',
'password',
'role_id',
'language_id'
];
public function language() {
return $this->belongsTo(Language::class);
}
and on user creation
$user = User::create(
'email' => ...
'password' => ...
'role_id' => ...
'language_id' => $input['language']
);
you can see more here https://laravel.com/docs/8.x/eloquent-relationships#the-save-method, so in this case if you want to use the save method on this relation you must do it like
$language = Language::find($input['language']);
$language->user()->save($newuser);
I suggest use the Laravel documentation sintax for the relationship in migration
$table->foreignId('language_id')->constrained()->nullable();

In Laravel many-to-many relationship, is there a way to update data in a pivot table?

I built two models User and Institution.
How do I update the pivot data between them, after adding additional Pivot columns?
<?php
class User extends Authenticatable
{
public function institutions()
{
$pivots = ['id', 'program_choice', 'session'];
return $this->belongsToMany('App\Institution')
->withPivot($pivots);
}
}
class Institution extends Authenticatable
{
public function users()
{
$pivots = ['id', 'program_choice', 'session'];
return $this->belongsToMany('App\User', 'institution_user')
->withPivot($pivots);;
}
}
Here are the migrations
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->integer('user_id')->unsigned();
$table->rememberToken();
$table->timestamps();
});
}
class CreateInstitutionsTable extends Migration
{
public function up()
{
Schema::create('institutions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
$table->string('city')->nullable();
$table->string('state')->nullable();
$table->string('country')->nullable();
$table->string('address')->nullable();
$table->string('user_id')->nullable();
$table->string('postal_code')->nullable();
$table->timestamps();
});
}
}
This is the what the pivot table looks like
I am able to attach the information to the pivot table
public function storeInstitution(Request $request)
{
$user_id = auth()->user()->id;
$user = User::find($user_id);
$institution_id = $request->input('institution_id');
$user_program_choice = $request->input('program_choice');
$user_session = $request->input('session');
$user_inst = array(
'program_choice' => $user_program_choice,
'session' => $user_session,
'user_id' => $user_id,
'institution_id' => $institution_id
);
$user->institutions()->attach($institution_id, $user_inst);
return 'success';
}
But unable to update the attached pivot E.g I can't change the program_choice, particle physics to something like digital art
Here's my current code
public function updateInstitutions(Request $request, $pivot_id)#TODO id is pivot_id
{
$user_id = auth()->user()->id;
$user = User::find($user_id);
$institution_id = $request->input('institution_id');
$pivot_attributes = array(
'user_id' => $user_id,
'institution_id' => $institution_id,
'session' => $request->input('session'),
'program_choice' => $request->input('program_choice'),
);
$user->institutions()->updateExistingPivots($institution_id, $pivot_attributes, false);
return 'success';
}
How do I update my pivot data, using the pivot id?
Check the documentation regarding this aspect.
Updating A Record On A Pivot Table
If you need to update an existing row in your pivot table, you may use
updateExistingPivot method. This method accepts the pivot record
foreign key and an array of attributes to update:
$user = App\Models\User::find(1);
$user->roles()->updateExistingPivot($roleId, $attributes);

Relationships retrieve user emails

I'm trying to understand how I can join the tables users and teams to members in fact ?
Users Table:
id | name | email | password
1 alain alain#gmail.com *****
2 eric eric1#gmail.com *****
Teams Table:
id | name
1 R.O.S
2 Stephanois
Members Table:
id | user_id (email) | team_id (name)
1 1 1
2 2 1
DataBase:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
public function up()
{
Schema::create('members', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned()->nullable();
$table->foreign('user_id')->references('id')->on('users');
$table->integer('team_id')->unsigned()->nullable();
$table->foreign('team_id')->references('id')->on('teams');
$table->timestamps();
});
}
public function up()
{
Schema::create('teams', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('user_id')->unsigned()->nullable();
$table->foreign('user_id')->references('id')->on('users');
$table->timestamps();
});
}
User Model:
protected $fillable = [
'name', 'email', 'password',
];
public function team(){
return $this->hasOne('App\Team', 'user_id', 'id'); ?
}
public function member(){
return $this->hasOne('App\Member', 'user_id', 'id'); ?
}
Team Model:
protected $fillable = [
'user_id', 'name'
];
public function member(){
return $this->hasMany('App\Member', 'team_id'); ?
}
public function user(){
return $this->belongsTo('App\User', 'id', 'user_id'); ?
}
Member Model:
protected $fillable = [
'id', 'user_id', 'team_id'
];
public function team(){
return $this->belongsTo('App\Team', 'team_id');
}
public function user(){
return $this->belongsTo('App\User', 'id', 'user_id'); ?
}
I thank you in advance for your help
Edit: when I log in with the user 'alain#gmail.com' I don't retrieve the information for the user 'alain#gmail.com' ? ie user_id and team_id.
public function index(Request $request)
{
$user = $request->user();
$members = Member::query()
->when($user->hasRole('admin') !== true, function (Builder $query) use ($user) {
$query->where('id???', $user->email); ????
})
->when($request->has('search'), function (Builder $query) use ($request) {
$query->where('name??', 'like', '%' . $request->input('search') . '%');
})->with('team:id,name')
->paginate(5);
return view('admin.members.index', compact('members'))
->with('display_search', $user->hasRole('admin'));
}
Which one is you're trying to achieve is not enough clear. If I guess, you want to find out which user is in which team then, in this situation you can go through -
User.php model -
protected $fillable = [
'name', 'email', 'password',
];
public function member(){
return $this->belongsTo('App\Member', 'user_id', 'id');
}
Member.php model -
protected $fillable = [
'id', 'user_id', 'team_id'
];
public function team(){
return $this->belongsTo('App\Team', 'team_id');
}
public function user(){
return $this->belongsTo('App\User', 'user_id', 'id');
}
Team.php model (keep as it is for now)-
class Team extends Model
{
protected $fillable = [
'name'
];
}
And finally call your members those are already in a team in your controller method-
$members = Member::with('team', 'user')->get();
return view('your-blade-file-name', compact('members'));
And, in your blade file -
#foreach($members as $member)
<p>Name: {{ $member->user->name }} , Email: {{ $member->user->email }} , Team - {{ $member->team->name }}</p>
#endforeach
Output:
Name: alain, Email: alain#gmail.com , Team - R.O.S
Name: eric, Email: eric1#gmail.com , Team - R.O.S
If you want a user can belong to a single team, then you don't need an intermediate table. Just add a column in the user table like team_id that will hold the value of which team the user belong.
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->unsignedInteger('team_id'); //make it nullable or anything based on your need
$table->rememberToken();
$table->timestamps();
});
Schema::create('teams', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
And now make the relationships in the models:
User Model
protected $fillable = [
'name', 'email', 'password','team_id'
];
public function team()
{
return $this->belongsTo('App\Team', 'team_id');
}
Team Model
protected $fillable = [
'name'
];
public function users()
{
return $this->hasMany('App\User', 'team_id');
}
In the above relationships a user belongs to a single team and a team has many users. I am pretty sure you are trying to achieve this. Feel free to ask if you find anything hard to understand.
Look you don't need the intermediate model Member. If you want a many to many relationship then you need to use an intermediate table.

Not being relationships in laravel 5 db:seed

I have three model Developer model:
migration:
$table->increments('id');
$table->string('email')->unique();
$table->unsignedInteger('programming_language_id');
$table->unsignedInteger('language_id');
$table->timestamps();
and function
class Developer extends Model
{
public function programming_languages() {
return $this->hasMany('App\ProgrammingLanguage');
}
public function languages() {
return $this->hasMany('App\Language');
}
}
ProgrammingLanguage model:migration:
$table->increments('id');
$table->string('name')->unique();
$table->timestamps();
and function:
protected $table = 'programming_languages';
public function developers() {
return $this->belongsToMany('App\Developer');
}
Language model: migration:
$table->increments('id');
$table->string('code', 30)->unique();
$table->timestamps();
and function:
public function developers() {
return $this->belongsToMany('App\Developer');
}
I want to make db seeder and being relationships between those. How can i do that?
I am trying: creating a factory DeveloperFactory
$factory->define(App\Developer::class, function (Faker $faker) {
return [
'email' => $faker->unique()->safeEmail,
'programming_language_id' => function () {
return factory(App\ProgrammingLanguage::class)->create()->id;
},
'language_id' => function () {
return factory(App\Language::class)->create()->id;
}
];
});
seed OK, But it doesnot make any relationships. just seed the data.How can i do that by db:seed?
Seed your languages and programming languages tables first, then query for records in the developer seeder.
return [
'email' => $faker->unique()->safeEmail,
'programming_language_id' => App\ProgrammingLanguage::inRandomOrder()->first()->getKey(),
'language_id' => App\Language::inRandomOrder()->first()->getKey()
];

create a post with pre-defined categories

I have a table for posts and a table for categories. But its not possible to create new categories they should already exist in the database and is not possible to change them. DO you know how to structure this logic properly on laravel?
Also when a post is created it should have a maximum of 3 categories and a published date. Do you know how to do that validation in laravel, of dates and the maximum of 3 categories when a post is created? I have this validation for now:
$this->validate($request, [
‘post_name’ => 'required|max:255',
‘post_categories’ => 'required',
‘post_date' => 'required',
]);
Relevant code for this context:
// Category Model:
class Category extends Model
{
public function posts(){
return $this->belongsToMany('App\Post’);
}
}
// Post model
class Post extends Model
{
public function categories(){
return $this->belongsToMany('App\Category');
}
}
Migration Post:
class CreatePost extends Migration
{
public function up()
{
Schema::create(‘posts’, function (Blueprint $table) {
$table->increments('id');
$table->string(‘name’);
…
$table->timestamps();
});
}
Migration Category:
<?php
class CreateCategoriesTable extends Migration
{
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
}
// post controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
}
public function create()
{
return view(‘app.createPost’);
}
public function store(Request $request)
{
dd($request->all());
$this->validate($request, [
‘post_name’ => 'required|max:255',
‘post_categories’ => 'required',
‘post_date' => 'required',
]);
}
Use between rule
'post_categories' => 'required|array|between:1,3'
And for the first question, use firstOrCreate
foreach($request->post_categories as $categoryName){
$category= App\Category::firstOrCreate(['name' => $categoryName]);
$post->categories->attach($category->id);
}

Resources