A bug in relations with pivot table Laravel - php

I need to make a relationship between User and Subscription table.
The user who registers gets a default free user and sees 1 an offer.
If silver see 8 offers, gold 15, platinum 20.
I made a relationship between user and subscription tables with a pivot table subscription_user.
The first question is whether I made a mistake somewhere in relation the model?
The second question is how to return only one offers by default, or if you subscribe to 8 offers (silver), 15 (gold), 20 (platinum) and in which controller?
User table:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('subscription')->default('free');
$table->rememberToken();
$table->timestamps();
});
Subscription table:
Schema::create('subscriptions', function (Blueprint $table) {
$table->increments('id');
$table->string('subscription');
$table->integer('offers');
$table->timestamps();
});
Sabscription_user table:
Schema::create('subscription_users', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned()->index(); //user table
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); //foreign key relation
$table->integer('subscription_id')->unsigned()->index();
$table->foreign('subscription_id')->references('id')->on('subscriptions')->onDelete('cascade'); //foreign key relation
});
Subscription model:
class Subscription extends Model
{
public function users() {
return $this->belongsToMany('App\User');
}
}
User model:
public function subscriptions(){
return $this->belongsToMany('App\Subscription');
}
Is everything connected as it should be for many to many relationship?
In which controller I can get information about offers?

By convention the pivot table is named singular so you will need to specify by adding a second argument. I like to include all the arguments so there is no guessing anyway.
public function subscriptions()
{
return $this->belongsToMany("App\Subscription", 'subscription_users', 'user_id', 'subscription_id', 'id', 'id');
}
public function users()
{
return $this->belongsToMany("App\User", 'subscription_users', 'subscription_id', 'user_id', 'id', 'id');
}
Yes you should use a controller but it depends on where you are trying to display this. I imagine you will need to display the current subscriptions somewhere as well as add and remove.

Related

Why am I getting a column not found error when trying to return a collection model based on user ID?

I'm trying to return all Threads that a given User has participated in.
The endpoint accepts a userId and supposed to return a collection of thread models.
However, I keep getting this error when executing the controller action. It's looking for a message_id column but I don't have that defined on the thread table or on any table, for that matter - making this a weird error.
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'thread.message_id' in 'where
clause' (SQL: select * from `thread` where `thread`.`message_id` = 2 and
`thread`.`message_id` is not null)"
I believe there might be something off with how I'm linking the tables but I'm not entirely sure. I'd assume the message table's column thread_id should reference the id column on the thread table which's what I thought I was doing in the message migration below.
What am I doing wrong and how can I fix this?
Here's users migration:
Schema::create('users', function (Blueprint $table) {
$table->id('id');
$table->string('email')->unique();
$table->string('full_name');
$table->string('password');
});
Here's thread migration:
Schema::create('thread', function (Blueprint $table) {
$table->id();
$table->string('title');
});
Here's message migration:
Schema::create('message', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('thread_id');
$table->string('body');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
$table->foreign('thread_id')
->references('id')
->on('thread')
->onDelete('cascade');
});
controller action:
public function getUserThreads($userId) {
$userParticipatedThreads = Message::findOrFail($userId);
return $userParticipatedThreads->thread;
}
message model:
public function thread() {
return $this->hasMany(Thread::class);
}
endpoint:
[GET] http://127.0.0.1:8000/api/getUserThreads/2
Route::get('getUserThreads/{userId}', [ThreadController::class, 'getUserThreads']);
Your thread relationship on your Message class is looking for the message_id, since that's the default way the hasMany relationship works. You'll need to override the column that it's basing the relationship off of.
public function thread() {
return $this->hasMany(Thread::class, 'id', 'thread_id');
}
However, since it looks like the message belongs to one single thread (each message has a thread_id), then you actually want belongsTo instead
public function thread() {
return $this->belongsTo(Thread::class);
}
To answer the question;
Why am I getting a column not found error when trying to return a
collection model based on user ID?
Because your thread table doesn't have a message_id field defined on it.
Schema::create('thread', function (Blueprint $table) {
$table->id();
$table->string('title');
});
A Message belongs to a Thread but you seem to have that relationship inverted.
On your Thread model, define a relationship to the Message model:
public function messages()
{
return $this->hasMany(Message::class);
}
Then you can query for the existance of some message by user:
$messages = Thread::whereHas('messages', function ($query) use ($userId) {
$query->where('user_id', $userId);
})->get();

SQL Server ON DELETE CASCADE Error In Laravel

I'm using Laravel Framework; I have multiple connected tables,
User Schema 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();
});
Article Schema Table :
Schema::create('articles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->string("title");
$table->string("slug")->unique();
$table->text("cover");
$table->text("body");
$table->boolean("status")->default(1);
$table->timestamps();
});
Comment Schema Table :
Schema::create('comments', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger("user_id");
$table->foreign("user_id")->references("id")->on("users")->onDelete('cascade');
$table->unsignedBigInteger("article_id");
$table->foreign("article_id")->references("id")->on("articles")->onDelete('cascade');
$table->text("comment");
$table->bigInteger("level");
$table->boolean("status")->default(0);
$table->timestamps();
});
In the comments table I have this important code:
$table->unsignedBigInteger("user_id");
$table->foreign("user_id")->references("id")->on("users")->onDelete('cascade');
$table->unsignedBigInteger("article_id");
$table->foreign("article_id")->references("id")->on("articles")->onDelete('cascade');
I want to when I delete an article or user, I would like to have their comments deleted when a user or an article is deleted, But this is a mistake in the SQL Server database.
Error Form SQL Server Database:
enter image description here
When I removed this method
->onDelete('cascade');
The error disappears.
I definitely need to have the corresponding method. Can anyone guide me to solve this problem?
Use a delete observer instead, on delete cascade is not the best thing to do.
Define the relationship in User model:
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function articles()
{
return $this->hasMany(Article::class, 'user_id');
}
Then implement the boot method to observe the deletion of a record
protected static function boot() {
parent::boot();
static::deleting(function($user) {
$user->articles()->delete();
});
}
You can loop on articles comments in the same method or do the same thing you did here with Article and Comment models
Read more about observers here https://laravel.com/docs/5.8/eloquent#observers
It seems that you are using cascade on delete even for the user who access it. I don't think you need to delete the list even if the user who is not the creator but only have access to it. So update following
Change
$table->foreign("user_id")->references("id")->on("users")->onDelete('cascade');
to
$table->foreign("user_id")->references("id")->on("users")->onDelete('no action');

How should i create a third table for new Categories in Laravel

I'm developing a practice project where I have 2 tables. An article table with heading and text and a user table. Now I want an admin (which I created in the database before) to create additional Article Categories. Does anyone have an idea how I can link my tables or my models now ? I've only been working with Laravel for a week now and don't quite look through it yet.
That are my tables :
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('heading',80);
$table->string('clubchoose')->default('DEFAULT');
$table->text('text');
$table->timestamps();
$table->unsignedInteger('author')->nullable();
$table->foreign('author')->references('id')->on('users');
$table->string('image')->nullable();
});
}
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->Increments('id');
$table->boolean('isAdmin')->default(false);
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
So in order to achieve that, you first need to create your categories model/controller/table:
php artisan make:model Category -m //-m flag creates migration automatically
php artisan make:controller CategoryController --reosource //--resource flag creates a CRUD controller.
After that, inside your categories table, add column article_id (and other fields you want):
public function up()
{
Schema::table('categories', function (Blueprint $table) {
// ...
$table->integer('article_id')->nullable();
});
}
After that, you need to alter your articles table by adding category_id column. To do that, create a new migration to add that just one column:
php artisan make:migration add_category_id_to_articles_table --table==articles
Inside that migration, add this:
public function up()
{
Schema::table('articles', function (Blueprint $table) {
$table->integer('category_id')->nullable();
});
}
Now that you have migrations set up, you need to set up relations. Inside your Article model, add this relation:
public function category(){
return $this->belongsTo('App\Category');
}
So an article will belong to category. For the categories, as they will have more than one article, add this relation:
public function articles(){
return $this->hasMany('App\Article');
}
So now, you have everything set up. Run migrations, and add some categories and test things out. Let me know if you have any errors or troubles.
EDIT:
If you want only an admin to add new categories, you could create a button that is only visible for admins. Something like this:
#if(Auth->user()->role == 'admin'){
//show button
#endif

PHP Laravel 5.2 belongs to

I have 3 tables Users, Profiles, and Friends.
Users contains users, obviously.
Then I got the Profiles and Friends table (see below).
public function up()
{
Schema::create('profiles', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->string('picture')->nullable();
$table->string('status')->nullable();
$table->string('text')->nullable();
$table->boolean('show_sex');
$table->boolean('show_dob');
$table->timestamps();
});
}
public function up()
{
Schema::create('friends', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id_sender')->unsigned();
$table->integer('user_id_receiver')->unsigned();
$table->foreign('user_id_sender')->references('id')->on('users');
$table->foreign('user_id_receiver')->references('id')->on('users');
$table->integer('status'); // 1 = friends, 2 = under validation
$table->timestamps();
});
}
As you can see, i created some foreign keys that relates to the Users table.
The Friends table contains all friendships between users (the status field will determine if the friendship is under validation or validated).
I have the default User model that comes with Laravel 5.2 and was wondering, how can I easely get all the friendships, that belongs to the signed user?
Could I use something like belongsTo() or something to easely get all friendrequests where the user_id_receiver field is the same as the signed users id? I didn't quiet understand the documentation for hasOne or belongsTo.. Would be nice if someone could clearify how it actually works.
Thanks in advance.
Yes, you should use One-to-one relation between User and Profile models and One-to-many relation between User and Friend models. Add this to both models - Friend and Profile:
public function user()
{
return $this->belongsTo('app\User');
}
And add this to User model:
public function profile()
{
return $this->hasOne('app\Profile');
}
public function friend()
{
return $this->hasMany('app\Friend');
}
And then you could use Eloquent to get data:
$signedUserId = Auth::user()->id;
$currentUserFriendRequests = Friend::where('user_id_receiver', $signedUserId)->get();
I hope this will be helpful.

laravel cascade not working?

I'm building a ticketsystem with laravel. But when I remove a ticket the reactions don't go away?
This is my migration of the pivot table:
public function up()
{
Schema::create('reactions_ticket',function(Blueprint $table)
{
$table->integer('ticket_id')->unsigned();
$table->foreign('ticket_id')->references('id')->on('ticket')->onDelete('cascade');
$table->integer('reactions_id')->unsigned();
$table->foreign('reactions_id')->references('id')->on('reactions')->onDelete('cascade');
});
}
And this is my reaction table:
public function up()
{
Schema::create('reactions',function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->text('content');
$table->timestamps();
});
}
and my ticket table:
public function up()
{
Schema::create('ticket',function(Blueprint $table)
{
$table->increments('id');
$table->string('slug')->nullable();
$table->integer('ticketid')->unsigned();
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('subject_id')->unsigned();
$table->foreign('subject_id')->references('id')->on('subject')->onDelete('cascade');
$table->integer('websites_id')->unsigned();
$table->foreign('websites_id')->references('id')->on('websites')->onDelete('cascade');
$table->integer('status_id')->unsigned();
$table->foreign('status_id')->references('id')->on('status')->onDelete('cascade');
$table->text('content');
$table->timestamps();
});
}
What am I doing wrong>?
I would always prefer to handle such actions by myself instead of let id be done by MySQL.
You could use laravels event-handler inside your Ticket model.
protected static function boot() {
parent::boot();
static::deleting(function($ticket) {
// delete related stuff ;)
$reaction_ids = $ticket->reactions()->lists('id');
Reaction::whereIn($reaction_ids)->delete();
});
}
So you still have (if you want/need) the advantage of softdelete and more controll.
You have a many to many join between reactions and tickets. Therefore a ticket can have many reactions and a reaction could have many tickets. If you delete a ticket I would not expect to have the reactions deleted because those reactions could be attached to other tickets that are not deleted.
What will be deleted are the entries in the pivot table for that ticket. If you wanted to clean up all reactions that no longer have a ticket attached, that would be a separate task, mysql won't cascade that for you.
Your table names are not standard, Btw. They should be tickets, reactions, and reaction_ticket for the pivot table.

Categories