I have 3 tables, with the fields listed below:
Pricings
id
route_id
Routes
id
from_city_id
to_city_id
Cities
id
name
So far the relationships of fields are: pricings belong to a route, and routes belongs to city.
But I'm not sure about these relationships, since from_city_id and to_city_id are foreign keys relating to id on cities.
Maybe I'm wrong designing my table or something else.
route_id is a foreign key to id on the Routes table.
from_city_id and to_city_id are foreign keys to id on the Cities table.
How I can define relationships of these 3 tables so I can get from city name and to city name from the pricings model, like $pricing->from_city->name and $pricing->to_city->name?
Any help appreciated.
UPDATE:
My Pricing Model:
public function route()
{
return $this->belongsTo(Route::class);
}
My Route Model:
public function pricing(){
return $this->hasOne(Pricing::class);
}
public function zone(){
return $this->belongsTo(Zone::class);
}
public function city(){
return $this->belongsTo(City::class);
}
public function from_city(){
return $this->belongsTo(City::class);
}
public function to_city(){
return $this->belongsTo(City::class);
}
Now I can use $pricing->route->from_city->name and $pricing->route->to_city->name
It shows the correct result, but how can this be achieved using Laravel?
Does this mean Laravel will assume that the route table has fields to_city_id and from_city_id, since the method in the route model is to_city() and from_city()?
Thanks
One solution may be to make a migration (new table or to change to existing table).
https://laravel.com/docs/5.3/migrations
Laravel's schema build is super handy:
https://laravel.com/docs/5.0/schema
An example of the routes migration would be:
Make the migration:
php artisan make:migration routes
The migration would look something like:
```
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUserRole extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('routes', function (Blueprint $table) {
$table->increments('id');
$table->foreign('id')->references('route_id')->on('pricings')->onDelete('cascade');
$table->integer('from_city_id')->unsigned()->index();
$table->foreign('from_city_id')->references('id')->on('cities')->onDelete('no action');
$table->integer('to_city_id')->unsigned()->index();
$table->foreign('to_city_id')->references('id')->on('cities')->onDelete('no action');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('routes');
}
}
```
The above for some reason will not show correctly on here, so here is a cleaned up view link: http://viper-7.com/kfgUjt
Try this one:
In Pricing Model:
//if pricing and route have one to one(hasOne) relationship or you may change it to one to many(hasMany)
public function routes(){
return $this->hasOne('App\Routes','id','route_id');
}
and in Route Model:
public function from_city(){
return $this->hasOne('App\Cities','id','from_city_id');
}
public function to_city(){
return $this->hasOne('App\Cities','id','to_city_id');
}
Related
I'm creating an taggable table like so:
Schema::create('taggable', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tag_id');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
$table->unsignedBigInteger('company_id');
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->unsignedBigInteger('created_by')->nullable();
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
$table->timestamps();
});
As you can see, next to connecting tags to a Post, Video etc (as per the Laravel docs example), I'd also like to ensure that the row that's added is connected to a Company and User model so I can keep track who it belongs to and who created it, but even more so access properties from those models in controllers and views.
I know that in my Post model I can do:
public function tags()
{
return $this->morphedByMany(\App\Models\Tag::class, 'taggable')->withPivot('created_by', 'company_id', 'created_at');
}
The problem is that this will retrieve just the value for created_by and company_id and not the Eloquent model. Is this possible?
So what I'd like to do is access properties of those relationships in controllers and views like so:
$post = Post::findOrFail(1);
foreach($post->tags as $tag) {
$tag->created_by->name // accessing 'name' property on the `User` model
}
foreach($post->tags as $tag) {
$tag->company->address // accessing `address` property on the `Company` model
}
You must do like below:
first you must define relationship between tags and users
class Tags extends Model
{
public function taggable(): MorphTo
{
return $this->morphTo();
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
}
then for achieve that you want you must:
$post = Post::first();
$users = $post->tags()->with('createdBy')->get();
I can't access pivot model's id attribute. I have one pivot model PivotModel and two models that are connected through this pivot model
ModelA class:
public function modelB()
{
return $this->belongsToMany(ModelB::class, 'model_a_model_b', 'model_a_id', 'model_b_id')
->using(PivotModel::class)
->withPivot('id', 'prop_1', 'prop_2');
}
ModelB class:
public function modelA()
{
return $this->belongsToMany(ModelA::class, 'model_a_model_b', 'model_b_id', 'model_a_id')
->using(PivotModel::class)
->withPivot('id', 'prop_1', 'prop_2');
}
PivotModel:
use Illuminate\Database\Eloquent\Relations\Pivot;
class PivotModel extends Pivot
{
public $incrementing = true;
public static function boot() {
parent::boot();
static::saved(function ($model) {
dump($model->id);
dump($model->toArray());
});
}
}
Pivot table migration file
Schema::create('model_a_model_b', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('model_a_id');
$table->unsignedInteger('model_b_id');
$table->string('prop_1');
$table->string('prop_2');
$table->unique(['model_a_id', 'model_b_id'], 'model_a_id_model_b_id');
$table->foreign('model_a_id')
->references('id')->on('model_a')
->onDelete('cascade')
;
$table->foreign('model_b_id')
->references('id')->on('model_b')
->onDelete('cascade')
;
$table->timestamps();
});
I assume this should work.
This is from the official documentation for Laravel 5.8
Custom Pivot Models And Incrementing IDs
If you have defined a many-to-many relationship that uses a custom pivot model, and that pivot model has an auto-incrementing primary key, you should ensure your custom pivot model class defines an incrementing property that is set to true.
/**
* Indicates if the IDs are auto-incrementing.
*
* #var bool
*/
public $incrementing = true;
I can only access the prop_1 and prop_2 properties but not the id property.
The id is null
dump($model->id);
and the toArray() only shows other props but not the id
dump($model->toArray());
I found a temporary solution. If you know how to do it better please suggest.
As mentionied, the id property is accessible in the created() method
so you can easily get it using $model->id.
static::created(function ($model) {
dump($model->id);
});
The problem is in the updated() method where the $model instance is filled with properties other than id.
See the method updateExistingPivotUsingCustomClass inside of Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable
static::updated(function($model) {
// If the model is of type Pivot we have to store it into a different variable or overwrite it. If we need dirty props we first need to store them to separate variable before overwriting the $model variable
$dirtyProps = $model->getDirty();
if($model instanceof Pivot) {
$model = get_class($model)
::where($model->foreignKey, $model->{$model->foreignKey})
->where($model->relatedKey, $model->{$model->relatedKey})
->firstOrFail();
// Use the model
$model->id ...
}
});
I have two models, User and Service, with a ManyToMany relationship. The pivot table has two additional fields, id_code and alias. There may be many relationships between User and Service, but each one is uniquely identified by the field id_code. I need to retrieve a specific record in the pivot table by the id_code, and update the alias for that record only.
My models:
User:
class User extends Authenticatable
{
//All attributes & other functions here...
public function linked_services(){
return $this->belongsToMany(Service::class)
->withPivot('alias', 'id_code')
->withTimestamps();
}
}
Service:
class Service extends Model
{
//All attributes & other functions here...
public function linked_users(){
return $this->belongsToMany(User::class)
->withPivot('alias', 'id_code')
->withTimestamps();
}
}
Service-User migration:
Schema::create('service_user', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id')->nullable();
$table->unsignedBigInteger('service_id');
$table->string('alias', 50)->nullable();
$table->string('id_code', 50);
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('service_id')->references('id')->on('services');
$table->timestamps();
});
In the update function, I get the specific $id_code from a view, but I don't know how to update the "alias" for that specific "id_code".
What I already tried and is not working:
public function update(Request $request, String $id_code){
foreach(Auth::user()->linked_services as $service){
if($service->pivot->id_code === $id_code){
$service->pivot->alias = $request->alias;
$service->pivot->save();
}
}
return redirect()->route('services_user.index');
}
This function updates ALL the existing pivot records for the service_user, I need to update only the specific record for an given "id_code".
I solved it!!! I tell you how:
Auth::user()->linked_services()
->wherePivot('id_code', $id_code)
->update(['alias' => $request->alias]);
I learned that $user->linked_services() (unlike $user->linked_services) returns a relationship object (in this case, belongsToMany), which contains all the relationship data (intermediate table, pivots, FKs, parent / related objects, etc), and the desired pivot can be accessed on that object with ->wherePivot().
I'm trying to implement a morphable table for categories, right now I've the following.
// Snippet Table
- id
- title
- body
// Post Table
- id
- title
- body
// Category Table
- id
- name
I want to be able to morph the posts and snippets to have only one category, something like this:
// Categorizable Table
- category_id
- categorizable_id
- categorizable_type
Do I have to have another model for this categorizable table? Or there is a way to set it without another model?
So far I have this
class Snippet extends Model
{
public function category()
{
return $this->morphOne(Category::class, 'categorizable');
}
}
class Post extends Model
{
public function category()
{
return $this->morphOne(Category::class, 'categorizable');
}
}
class Category extends Model
{
public function categorizable()
{
return $this->morphTo();
}
}
And I have 4 tables, snippets, posts, categories, categorizables, the last one the migration looks like this.
Schema::create('categorizables', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('category_id');
$table->morphs('categorizable');
$table->timestamps();
});
The thing is, what is the correct way to save this relationship, I'm testing it on tinker and bothattach($category_id) and sync($category_id) aren't saving the relationship they return the error BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::categorizable()', What I'm missing?
Sorry I thought you wanted many-to-many relationship. morphOne relationship API is the same as morphMany from the docs
Post extends Model
{
public function category() {
return $this->morphOne(Category::class, 'categorizable');
}
}
Category extends Model
{
public function categorizable() {
return $this->morphTo();
}
}
EDIT
When using morphOne you don't need a pivot table categorizables table must be deleted and change your categories table to include the morph fields
// Category Table
- id
- name
- categorizable_id
- categorizable_type
In my site, I have a table of Users. Users can follow each other. A user can created any number of Post's.
I want to be able to see the most recent Post's from the users I've followed.
Currently my models are defined like this:
User Model:
class User extends Authenticatable
{
public function followers(){
return $this->hasMany('App\Follower', 'following');
}
public function following(){
return $this->hasMany('App\Follower', 'id');
}
public function posts(){
return $this->hasMany('App\Post', 'createdby');
}
}
Follower Model:
class Follower extends Model
{
public function postsFollowing(){
return $this->hasMany('App\Post', 'createdby', 'following');
}
}
Post Model:
class Post extends Model
{
public function user(){
return $this->belongsTo('App\User', 'createdby', 'id');
}
}
My tables are as such:
Table Name, column names
User id, name
Follower id, following
Post id, created_by
In the Follower table, id represents the user, and following represents the user being followed. If user 3 follows user 537, then id = 3, following = 537. Hope that made sense.
What I've tried:
User::following()->posts - doesn't work because
User::following() returns an Eloquent Collection object. You have
to loop through this
Looping through my followed users to get their Post's - This
doesn't work either since I want to get the top n entries sorted by
date.
Update #1
Follower Model (Updated)
class Follower extends Model
{
public function followingPosts(){
return $this->hasManyThrough(
'App\Post', 'App\Follower',
'following', 'createdby', 'id'
);
}
}
Controller
$user = Auth::user();
$posts = $user->followingPosts;
I updated followingPosts() in the Follower class with the above. The result: $posts is null
Update #2
I moved the followingPosts() to the User model:
public function followingPosts(){
return $this->hasManyThrough(
'App\Post', 'App\Follower',
'following', 'createdby'
);
}
Controller:
$user = Auth::user();
$posts = $user->followingPosts;
Now I just get all posts, even from the users I didn't follow.
Your requirement - "Users can follow each other. A user can created any number of Post's. Being able to list recent posts (limited to number) of followers or whom user is following".
You can define many-to-many relationship on the User Model (Many-To-Many relationship on self - User Model).
Create two Pivot tables
Follower-User Pivot Table
class CreateFollowerUserPivotTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('follower_user', function (Blueprint $table) {
$table->integer('follower_id')->unsigned();
$table->foreign('follower_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
$table->primary(['follower_id', 'user_id']);
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('follower_user');
}
}
Following-User Pivot Table
class CreateFollowingUserPivotTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('following_user', function (Blueprint $table) {
$table->integer('following_id')->unsigned();
$table->foreign('following_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
$table->primary(['following_id', 'user_id']);
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('following_user');
}
}
Then define the relationships in your User Model
class User extends Model
{
public function followers()
{
return $this->belongsToMany(User::class, 'follower_user', 'user_id', 'follower_id')->withTimestamps();
}
public function following()
{
return $this->belongsToMany(User::class, 'following_user', 'following_id', 'user_id' )->withTimestamps();
}
//Assuming posts table has user_id as foreign key
public function posts()
{
return $this->hasMany(Post::class);
}
//Assuming posts table has user_id as foreign key
public function recent_posts()
{
return $this->hasMany(Post::class)->take(10)->orderBy('created_at', 'desc');
}
}
Now to get who a given user is following
//Say for example we take the logged in user
$user = User::with('following.recent_posts')->whereEmail(auth()->user()->email);
foreach($user->following as $following)
{
$posts = $following->recent_posts;
}
Hope this is what you are trying to accomplish.
You can use has-many-through for this as:
public function followingPosts()
{
return $this->hasManyThrough(
'App\Post', 'App\Follower',
'follow', 'createdby', 'id'
);
}
Then you can access the posts as:
$user->followingPosts; // returns collection of post model
Note: Assuming you have a follow column in Follower table.