How to create an optimized model, tables and the relationship between them? - database

I have development custom project where have database tables:
users
candidates
companies
scopes
skills
Users table:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('nickname')->unique();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('type')->default(0); // 0 = candidate and 1 = company
$table->rememberToken();
$table->timestamps();
});
Candidates table:
Schema::create('candidates', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
$table->string('name')->nullable();
$table->string('lastname')->nullable();
$table->string('surname')->nullable();
$table->text('about')->nullable();
$table->timestamps();
});
Companies table:
Schema::create('companies', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
$table->unsignedInteger('logo');
$table->foreign('logo')
->references('id')
->on('images')
->onDelete('cascade');
$table->string('name')->nullable();
$table->text('about')->nullable();
$table->string('website')->nullable();
$table->integer('employees')->nullable();
$table->timestamps();
});
Scopes table:
Schema::create('scopes', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->timestamps();
});
Skills table:
Schema::create('skills', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->timestamps();
});
Here any users type have scopes and skills. I can solve this solution with creating one table:
Attributes table:
Schema::create('attributes', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
$table->morphs('model');
$table->timestamps();
});
Example data inside attributes table:
----------------------------------------
| id | user_id | model_id | model_type |
----------------------------------------
| 15 | 176 | 34458 | App\Scope |
----------------------------------------
| 29 | 245 | 17654 | App\Skill |
----------------------------------------
But in this solution there is one bad side for me, this is the repetition of the string name of the model on each line of the record in the table. For example:
Models table:
Schema::create('models', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->timestamps();
});
Note: It may be impossible to create a module with the name of the Model, so you may need to create it with a different name, for example: Reference
Example data inside models table:
-------------------
| id | name |
-------------------
| 10 | App\Scope |
-------------------
| 11 | App\Skill |
-------------------
After creating the models table, the structure of the attribute table changes to:
Attributes table:
Schema::create('attributes', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
$table->foreign('model_id')
->references('id')
->on('models')
->onDelete('cascade');
$table->unsignedInteger('content_id');
$table->timestamps();
});
Now how can I create the right relationship inside models to get candidates or company user types skills or scopes?
I have custom relationships inside Users model:
public function candidate()
{
return $this->hasOne(Candidate::class);
}
public function company()
{
return $this->hasOne(Company::class);
}
Now, I need inside Candidate and Company model like this relationships:
public function skills()
{
// Some code here
}
public function scopes()
{
// Some code here
}
In general get users skills or scopes like this:
$user->candidate->scopes
$user->company->skills
If you have another suggestion for solving my problem, please offer your suggestion. If I made a mistake somewhere, correct me if it is possible.

Methink your primary ORM construction error is in this line:
$table->boolean('type')->default(0); // 0 = candidate and 1 = company
This is not solvable with the use of Laravel's ORM. You will have to replace this line with
$table->unsignedInteger('candidate_id')->nullable()->index();
$table->unsignedInteger('company_id')->nullable()->index();
and start from there. As it can either be a candidate or a company, one of the values will be empty. Easy to check. As there are only 2 possible options, a polymorphic relationship is not really needed. Once you get over that check, your ORM hierarchy will outline itself.
You only have to take care that if a candidate becomes a company or visa versa, you make sure the other value gets nullified. This can all be done in Model's mutators or a personalized public function so you don't have to worry about it in the Controllers/Views.
For example $user->choice->scopes; where you add a User Model function getChoiceAttribute()
public function getChoiceAttribute() {
if ($this->candidate) {
return $this->candidate;
}
if ($this->company) {
return $this->company;
}
return null;
}
The same applies to a setter function setChoiceAttribute() in case a candidate becomes a company. This only applies if you save or update a record.
To know in the view if you are working with candidates or companies, just check on $user->choice->getTable();.

Related

Inserting date and time as "2021-05-13T12:31" in database?

I am having a datetime input in my form and the user will select date and time from that input. But when inserting, the data should be only date and time, but T also getting inserted? How not to insert it?
Here is the migration code:
public function up()
{
Schema::create('competitions', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('desc');
$table->float('entry_fees');
$table->varchar('start_date_time',50);
$table->Date('end_date_time',50);
$table->integer('status');
$table->string('date_time');
$table->timestamps();
});
}
The columns name are start_date_time and end_date_time?
If its just datetime can you not set your columns like this then carbon parse on creating
public function up()
{
Schema::create('competitions', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('desc');
$table->float('entry_fees');
$table->datetime('start_date_time');
$table->datetime('end_date_time');
$table->integer('status');
$table->string('date_time');
$table->timestamps();
});
}
first your columns should be dateTime type in your table and then
you can change its format with laravel carbon
$createdAt = Carbon::parse($item['start_date_time']);
Then you can use
$start_date = $createdAt->format('d/m/Y H:i:s');

How to get the name of role from pivot table for a users in laravel?

Admin.php [Middlewere]
public function handle($request, Closure $next)
{
if (Auth::check() && Auth::user()->role->name == 'admin') {
return $next($request);
}
return Redirect::route('home');
}
I want to check if the column name of roles table is equal to admin.
I have tried with Auth::user()->role->name == 'admin').
Error I am getting
Property [name] does not exist on this collection instance.
Reference Model
User.php [Model]
public function role()
{
return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id')->withTimestamps();
}
Reference table
users table
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('photo')->nullable();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->longText('cartitems')->nullable();
$table->longText('wishlist')->nullable();
$table->unsignedBigInteger('discount')->default(0);
$table->rememberToken();
$table->timestamps();
});
This is users table. Notice there is no role directly here.
roles table
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('display_name');
$table->timestamps();
});
This is roles table. And every user have a role such as Superadmin, admin, seller orcustomer
role_user table
Schema::create('role_user', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedBigInteger('role_id');
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
$table->timestamps();
});
In this pivot table make a relationship between users and roles table.
That's because you're trying to access a value on collection and your laravel relationship is BelongsToMany but rather HasOne/BelongsTo. You should rather pluck all the names and then check if it exist in_array
$roles = Auth::check() ? Auth::user()->role->pluck('name')->toArray() : [];
if (in_array('admin', $roles)) {
return $next($request);
}
I would recommend changing the role relationship to roles rather because a user can have many roles in this context.
you can use whereHas
if (Auth::check() && User::where('id',Auth::user()->id)->whereHas('role',function ($query){
$query->where('roles.name','=','admin');
})->first()!=null ) {
return $next($request);
}
It's because a user can have many roles as per your schema design.
User <=> Role : Many to Many Relation
So Auth::user()->role gives a collection of instances of Role Model. And you're trying to access name property on the collection which can't be done.
Change the if condition to this
if (Auth::check() && Auth::user->role()->where('name', 'admin')->exists()) {
I'd also suggest you to change the name of the relation from role to roles in your User Model. as roles is more informative about the type of relation user and role share.
public function roles(){
return $this->belongsToMany(App\Role::class, 'role_user' 'user_id', 'role_id');
}
Use whereHas
$role = User::where('id',Auth::user()->id)->whereHas('role',function ($query){
$query->where('name','=','admin');
})->first();
if (Auth::check() && isset($role) && !empty($role) ) {
return $next($request);
}

Foreign key constraint is incorrectly formed - MariaDB & Laravel

I am running a databse migration in laravel 5.8 and I am getting the following error:
Illuminate\Database\QueryException : SQLSTATE[HY000]: General error: 1005 Can't create table cartorque.likes (errno: 150 "Foreign key constraint is incorrectly formed") (SQL: alter table likes add constraint likes_
post_id_foreign foreign key (post_id) references posts (id) on delete cascade on update cascade)
Here are how my tables are setup
User Table:
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->rememberToken();
$table->timestamps();
});
Posts Table
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->string('title', 500);
$table->longText('content');
$table->tinyInteger('privacy')->default('0');
$table->string('location', 250);
$table->bigInteger('longitude');
$table->bigInteger('latitude');
$table->bigInteger('user_id')->unsigned()->index();
$table->string('slug', 250);
});
Schema::table('posts', function (Blueprint $table) {
$table->foreign('user_id')->references('id')->on('users')
->onDelete('cascade')
->onUpdate('cascade');
});
Likes Table
Schema::create('likes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('post_id');
$table->boolean('like');
});
Schema::table('likes', function (Blueprint $table) {
$table->foreign('post_id')->references('id')->on('posts')
->onDelete('cascade')
->onUpdate('cascade');
});
Schema::table('likes', function (Blueprint $table) {
$table->foreign('user_id')->references('id')->on('users')
->onDelete('cascade')
->onUpdate('cascade');
});
The Likes table is to have 2 foreign keys user_id and post_id. It works fine on user_id but I get the error on the post_id column
Found the issue and it was the posts table not being created. This happened because the order of the migration files. The likes foreign key creation was occurring prior to the posts table being created. I change the order of the migration files.

How to insert into mutiple tables with Laravel

I am trying to insert into 2 different tables(House and Contact) using a single method.
public function store(HouseRequest $request){
$dataForm = $request->all();
$house = House::create($dataForm); //Insert into House table
$contact = $house->contact()->create($dataForm['contact']); //Insert into Contact table
return back('dashboard/houses')->with('message', 'Success');
}
Here is my table Contact:
Schema::create('contacts', function (Blueprint $table) {
$table->integer('house_id')->unsigned();
$table->string('name', 255);
$table->char('phone', 11);
$table->timestamps();
$table->foreign('house_id')->references('id')->on('houses')->onDelete('cascade')
}
House Model:
public function contact(){
return $this->hasOne(Contact::class);
}
It works fine (inserts into both tables), but after submitting the form, I got this:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'id' in 'where clause' (SQL: select * from contacts where id = 0 limit 1)
I don't know why it is running the query above. How can I get rid of this error?
You just forgot to add a column for ids add $table->increments('id'); in your migration :
Schema::create('contacts', function (Blueprint $table) {
$table->increments('id');
$table->integer('house_id')->unsigned();
$table->string('name', 255);
$table->char('phone', 11);
$table->timestamps();
$table->foreign('house_id')->references('id')->on('houses')->onDelete('cascade')
}

laravel schema hasTable always returns false (Sql Server)

for some reason i'm getting false when checking if an table exist in my tempdb, yes it does exist :)
tried:
Schema::hasTable('tempdb.dbo.tempreport')
Schema::hasTable('tempdb..tempreport')
this is what i want to do:
if(Schema::hasTable('tempdb.dbo.tempreport') == false){
Schema::create('tempdb.dbo.tempreport', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('airline');
$table->timestamps();
});
}

Resources