I'm very new to Laravel, and am having trouble understanding queries in Eloquent. I have two models - Laptop and Location. Suppose I want to query all the laptops belonging to a location that has a 'stock' value of 1. In SQL this would be a left join, but how do I do this in Eloquent? I've already added the relationships to the two models, so the Laptop model has a 'location' method, but where do I go from there?
You can use query scopes for that. They are thoroughly documented in the Laravel documentation
In the laptop model you can write a scope for the stockcheck:
public function scopeInStock($query)
{
return $query->where('stock', 1);
}
To keep it clean you can then create another relation in the Location model:
public function laptopsInStock()
{
return $this->hasMany('Laptop')->inStock();
// or this way:
// return $this->laptops()->inStock();
}
Now to retreive the results there are a couple of options:
//all laptops
$location->laptops;
// in stock only
$location->laptopsInStock;
// eager loading
$location->with('laptopsInStock')->get();
Since you've not provided any code so I'm going to describe from basic.
Migrations
Locations Table
Schema::create('locations', function(Blueprint $table){
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Laptop Table
Schema::create('laptops', function(Blueprint $table){
$table->increments('id');
$table->string('name');
$table->integer('location_id')->unsigned();
$table->integer('stock');
$table->timestamps();
$table->foreign('location_id')
->references('id')
->on('locations')
->onDelete('cascade')
->onUpdate('cascade');
});
Models
Location Model
class Location extends Model {
public function laptops(){
return $this->hasMany('\App\Laptop');
}
}
Laptop Model
class Laptop extends Model {
public function location(){
return $this->belongsTo('\App\Location');
}
}
If you've created your app like this then there are several way to achieve the goal.
This will query all the laptop belonging to a location and that has a stock value 1.
1.
Laptop::where('location_id','some_location_id')->where('stock',1)->get();
2
Location::where('id','some_location_id')->with(['laptops' => function($query){
$query->where('stock',1);
}])->get();
I hope this would solve your problem. Feel free to comment and ask whatever question you have.
Related
I am confused about Laravel database relationship (unsigned id, foreign references, cascade).
Do I have to use relationship in class (like hasMany, hasOne) and in table migration (like foreign, references); for both of them too?
I had read some articles but they are not clear for me. what is the best way for best developing on an example?
As an example for category and blog post; how should it be or your best example please?
create_categories_table migration:
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
create_posts_table migration:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug');
$table->longText('description');
$table->timestamps();
});
}
and Post Model:
public function category()
{
return $this->belongsTo(Category::class);
}
thank you.
Technically you don't have to define relationships between related data in your database, however, it is good practice to do so and provides benefits such as data integrity and cascading. There are also tools that can reverse engineer your database to generate a visual representation of its structure including relationships if they have been defined.
Adding relationships in your migration files creates that relationship at the database level, informing the engine that there is a logical relationship between data. You can define a foreign key constraint in your migrations in many ways, however, as of Laravel 7.x there is a forieignId method and a constrained method providing a simplified way of defining relationships from the previous method.
So for a basic example, to define a simple one-to-one relationship you might do:
public function up()
{
Schema::create('child', function (Blueprint $table) {
$table->foreignId('parent_id')->constrained();
});
}
Similarly, using Eloquent relationships to define relationships at the application level is not required. However, these helper methods provide a consistent and simplified implementation for managing related data that would otherwise require you to write additional code.
At least one but ideally both of the above would be used.
Update
if we need set a foreign key as a different key the using is just like
->constrained('privateName_categories'). two basic questions: * how Laravel understands that the reference table is 'categories' on your
example without writing its name?
The argument provided to constrained is not the name of the foreign key field but the name of the table the foreign key references. This is used in instances that either your foreign key or the table it references differ from the standard Laravel naming conventions and therefore cannot be inferred.
Laravel uses naming conventions to make connections between things. For foreign keys that convention has the format {table}_{id}. Laravel inspects the foreign key name and then uses the {table} element of the convention to create the relationship behind the scenes. If you're not using conventions you need to inform Laravel.
The following uses conventions to automatically create a relationship between the posts and categories table based on the value of the foreignId method argument.
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->foreignId('category_id')->constrained();
});
}
This example doesn't use a standard foreign key naming convention and so the related table needs to be provided to constrained.
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->foreignId('another_field')->constrained('categories');
});
}
Similarly, for scenarios in that you're not using conventions, you might need to specify the related column names in your Eloquent Model relationship. The exact syntax for this differs based on the type of relationship (hasOne, hasMany, manyToMany, etc.). You can read up on the different syntaxes for each relationship in Laravels defining relationships documentation.
you mention about reverse engineering tools
DataGrip by JetBrains can inspect the schema of your database and produce a visual representation. Others are also available, just do a search of the internet.
The role of migrations is to build your database schema (INSERT TABLE, ALTER TABLE, FOREIGN KEYS, DROP COLUMN, etc...).
The role of relations in Eloquent Model is to make Model aware of relations with other models. It provides a convenient way to query related models.
Relations will not create foreign keys for you.
For example if a post can be in only one category and a category contains many posts :
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('category_id');
$table->string('title');
$table->string('slug');
$table->longText('description');
$table->timestamps();
$table->foreign('category_id')->references('id')->on('categories');
});
}
Then, in your Post model :
public function category()
{
return $this->belongsTo(Category::class);
}
If you need you can also add this to Category Model :
public function posts()
{
return $this->hasMany(Post::class);
}
This will allow you to easily retrieve all posts of a category for example :
public function getPostsByCategory(int $categoryId)
{
$posts = Category::find($categoryId)->posts()->get();
return $posts;
}
EDIT :
I think the main interest is when you want to eager load relations.
For example you have posts in a blog.
Each post have many categories. (a post can be in many categories).
Each category have many posts.
Each post have many comments
In homepage I want to display last 5 posts with for each post their categories and comments.
If I build well designed relations, I can do this:
public function getLatestPosts()
{
return Post::query()
->with(['categories', 'comments'])
->orderBy('created_at', 'desc')
->limit(5)
->get();
}
It will get last 5 posts and it will attach categories and comments to each Post model.
I've got a problem i can't get through, here are my models:
Cloth.php
public function selling(): BelongsTo
{
return $this->belongsTo(Selling::class);
}
Selling.php
public function clothes(): HasMany
{
return $this->hasMany(Cloth::class);
}
And now it's anything ok and pretty basic... but then came this model:
Accessory.php
public function selling(): BelongsTo
{
return $this->belongsTo(Selling::class);
}
And now it's the problem: I need (i think) a polymorphic relationship but i can't understand how to make it in this specific case.
I have 2 starting models to morph to 1 model but every example i found have 1 starting model to morph to 2 models.
Do i need a polymorphic relationship?
I can't really get out of this.
Thanks!
You are basically looking for a one to many polymorphic relationship. Here is how to do it:
Let's say your tables are structured like bellow;
Schema::create('sellings', function (Blueprint $table) {
$table->id();
$table->integer('relation_id');
$table->string('relation_type');
$table->timestamps();
});
Schema::create('accessories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('details');
$table->timestamps();
});
Schema::create('cloths', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description');
$table->timestamps();
});
Selling.php
public function relation(){
return $this->morphTo();
}
Cloth.php
public function selling(){
return $this->morphOne(Selling::class, 'relation');
}
Accessories.php
public function selling(){
return $this->morphOne(Selling::class, 'relation');
}
Then, you can query using bellow approach;
$selling = Selling::findOrFail(1)->relation;
Now when you dd($selling) you get exactly what you are looking for from a correspondent table;
Please remember that the relation_type field needs to exactly correspond the model. See bellow screenshot for example;
What happens here is when you create a polymorphic function called test the database fields need to follow with test_type corresponding to model and test_id corresponding to the id of the model/database table.
The weekends table will be created first so I am thinking an weekendsteam_id needs to be in weekends and do a select statement. But I am getting really confused. Basically the user will enter a weekend. Then on a different view they will enter the team for that weekend. I need to find a way to tie the two tables together so I can query it for the weekend view on the front end.
Weekends Table
Schema::create('weekends', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->mediumText('verse');
$table->string('songtitle');
$table->string('songvideo');
$table->string('image')->default('default.png');
$table->string('videolink')->nullable();
$table->timestamps();
});
Weekends Team
Schema::create('weekendteam', function (Blueprint $table) {
$table->id();
$table->string('firstname');
$table->string('lastname');
$table->string('position');
$table->timestamps();
});
This sounds like a good use for a one-to-many relationship. Since it looks like you are storing the team members and their positions in the weekendteam table, you can simply add a weekend_id column (I also renamed the table to weekend_team_members):
Schema::create('weekend_team_members', function (Blueprint $table) {
$table->id();
$table->foreignId('weekend_id');
$table->string('firstname');
$table->string('lastname');
$table->string('position');
$table->timestamps();
});
Then, in order to get the team for any weekend, you can use a query like this:
SELECT * FROM weekend_team_members WHERE weekend_id = 1;
Which will fetch all of the team members for the weekend with that ID. If you are using Eloquent models, you can also use their built-in relationships to make querying easier:
class Weekend extends Model {
...
public function weekendTeamMembers() {
return $this->hasMany(WeekendTeamMember::class);
}
}
class WeekendTeamMember extends Model {
...
public function weekend() {
return $this->belongsTo(Weekend::class);
}
}
Once these relationships are defined in the models, you can use them to help you query the data. For example, if you already have a Weekend in the variable $weekend, you can fetch all of the team members like this:
$teams = $weekend->weekendTeamMembers;
Or, if you have a team member, you can get the weekend like this:
$weekendTeamMember->weekend;
I am trying to select the picture with the most likes within a specific category from my database. I have a database table storing the pictures and a table storing the likes. The pictures table is related to likeable table through a hasMany polymorphic relationship.
Like model:
public function likeable()
{
return $this->morphTo();
}
Picture model:
public function likes()
{
return $this->morphMany('App\Like', 'likeable');
}
Home Controller:
Picture::where('picture_type', 'food')->orderBy(likes(), 'desc')->first();
Likeable Migration:
public function up()
{
Schema::create('likeable', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('likeable_id');
$table->string('likeable_type');
$table->timestamps();
});
}
So far, the above line of code, in my home controller returns an error. From what I understand, the likes method has to be called on a specific instance. However, I do not know how to implement this all the while keeping my where clause.
Thank you for your help.
I assume you are wanting to order pictures by the most likes.
In which case, you want something like this:
Picture::withCount('likes')->where('picture_type', 'food')->orderBy('likes_count', 'desc')->first();
Having some problems retrieving nested relationship data. Here are my models:
class Partner extends Model
{
public function admins()
{
return $this->hasMany(Resource::class)->where('resource_type', 'Admin');
}
}
class Resource extends Model
{
public function details() {
return $this->belongsTo(ResourceDetail::class);
}
}
class ResourceDetail extends Model
{
}
When I try $this->partner->admins[0]->details it's giving null. The sql it generated is: "select * from resource_details where resource_details.id is null". I'm not quite sure why is it null in the query. I must have done something wrong with the relations. I tried $this->partner->with('admins.details')->find($this->partner->id)->toArray();. It's getting the admins, but details is still null. I also tried hasManyThrough, like: return $this->hasManyThrough(ResourceDetail::class, Resource::class)->where('resource_type', 'Admin'); it finds "unknown column". This is my database structure:
Schema::create('partners', function (Blueprint $table) {
$table->increments('id');
});
Schema::create('resources', function (Blueprint $table) {
$table->increments('id');
$table->integer('partner_id')->nullable()->unsigned();
$table->foreign('partner_id')->references('id')->on('partners')
->onUpdate('cascade')->onDelete('set null');
$table->enum('resource_type', constants('RESOURCE_TYPES'))->nullable();
$table->integer('resource_detail_id')->unsigned();
$table->foreign('resource_detail_id')->references('id')->on('resource_details')
->onUpdate('cascade')->onDelete('cascade');
});
Schema::create('resource_details', function (Blueprint $table) {
$table->increments('id');
});
Do I need to change the structure? Or, how can I get the data from current structure? All I want is, a partner has many resources, and a resource has one details.
From that error I think you may be trying to call $this->partner->admins[0]->details from a model that doesn't have an id. What is $this in context to?