I'm trying to understand the eloquent ORM.
I've created a basic post blog.
I want to add post types to my posts, each post should only have one type.
Post types:
News Post
Video Post
Blog Post
Database structure:
Table: archives
id
title
content
created_at
updated_at
excerpt
deleted_at
status
Table: types
id
name
created_at
updated_at
Table: archive_type
id
archive_id
type_id
created_at
updated_at
Models:
Model: Archive
class Archive extends Eloquent
{
protected $fillable = array('title', 'content', 'excerpt');
public function type()
{
return $this->belongsToMany('Type');
}
}
Model: Type
class Type extends Eloquent
{
protected $fillable = array('name');
}
This works, when running:
Archive::with(array('type'))->orderBy('id', 'DESC')->get();
But it returns a collection, I think this is wrong because it should only return one result.
The other problem I have is how to add a new row to the archive_type database for a new post.
Any help is appreciated.
A collection is always returned whether there is zero or 1 million results. The only exception to this rule is the "find" methods, where they are intended for use as a primary key lookup method, in which case, there can only ever be zero or one results, so it will return the model it finds instead of a collection. The behaviour your experiencing with your lookup is as expected.
If you would like to return the first model, you can place ->first() after your ->get().
To create a new archive type inc. the relation you should do:
// Get our archive and type models
$archive = Archive::with(array('type'))->orderBy('id', 'DESC')->get()->first();
$type = Type::where('name','=','video')->get()->first();
// attach them!
$archive->type()->attach($type);
Edit: How to display in blade template.
To pass the collection (or any data for that matter) through to the view you do this in your controller:
return View::make('yourview')->with('archives', $archives);
To loop through a collection in a blade template use #foreach
#foreach ($archives as $archive)
<p>This is archive id is {{ $archive->id }}</p>
#endforeach
otherwise if you've passed through just one model rather than a collection you can just do
<p>This is archive id is {{ $archive->id }}</p>
If your going to ask "how do i just show the first model from a collection in the view", the simple answer is, get the model out of the collection first then pass it to your view, anything else would require "business logic" in your view.
Related
I'm trying to create something like a blogging system inside the Laravel Nova.
I have a table named articles as well as abstract model AbstractArctile.
I also have 3 categories:
News - App\Models\News\Article extending the App\Models\Abstract\AbstractArticle
Digests - App\Models\Digests\Article extending the App\Models\Abstract\AbstractArticle
Offtopic - App\Models\Offtopic\Article extending the App\Models\Abstract\AbstractArticle
The table articles has a field named category, and there are 3 types of categories: news, digests, offtopic.
Besides the extending the abstract model, each resource model also has one attribute defined, which is it's category in the following manner:
/**
* To which category this article belongs to
* #var array
*/
protected $attributes = [
'category' => 'news'
];
I have no problem creating the articles under the specified categories in the Nova, however, instead of showing the articles from specified category, it displays articles from all categories on all resources.
Is there a way to display articles only from certain category on a given resource?
Summary
One abstract model -> 3 resources extending that model (with category attribute defined) -> how to display only items from that category inside the nova resource?
You can make use of Laravel eloquent query scope.
Add global scope like below to all 3 models (App\Models\News\Article, App\Models\Digests\Article, App\Models\Offtopic\Article), which is a easy way to make sure every query for a given model receives category constrains.
protected static function boot()
{
parent::boot();
static::addGlobalScope('category', function (Builder $builder) {
$builder->where('category', 'news'); // Change the value depends on the model
});
}
Hope this will help you.
In Nova, you can use indexQuery,detailQuery and editQuery as well and customize your resource results.
https://nova.laravel.com/docs/4.0/resources/authorization.html#index-filtering
I've made application with Laravel 5.5 and I use MySQL database. I have 2 tables
people: id, name, homeplanet_id
planets: id, name
And foreign key people.homeplanet_id references planets id
I also have 2 models: Person and Planet, and PersonController.
I can get Person's data in controller by using
Person::find($id)->getAttributes()
but it is like
[
id => 1
name => Name
homeplanet_id => 1
]
How can I get data look like next
[
id => 1
name => Name
homeplanet_id => Planet_name
]
You may need a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users.
https://laravel.com/docs/5.5/eloquent-resources
So, if you want to get JSON, use resources. If you want this data format for Blade views, it's a bad idea to transform it. Just work with the data in Blade template:
{{ $person->name }} lives on the planet {{ $person->planet->name }}
Where planet is name of relationship defined in Person model:
public function planet()
{
return $this->belongsTo(Planet::class, 'homeplanet_id')
}
Use with() method,
Something like
$person = Person::where(array())->with('planet')->get(); //You can fetch all related data by passing multiple values for method with()
To print
{{$person->planet->name}} //where planet indicated the relation on person model as follows
Where planet indicated the relation on Person model.
In our case
Getting data
$person = Person::where(array('id'=>$id))->with('planet')->get();
And in Person model do something like following to fetch planet using with() method:
Person extends model{
public function panet(){
//your relation one to one, one to many etc.....whatever
}
}
Try exploring eager loading in Laravel (eloquent) and scoped query too.
Visit the link for Eager loading and everything will be done by the ORM itself.Link
What I'm trying to do is to show the posts that have been saved by the user in the profile. I will try to explain it as good as possible refering to my code. So:
public function userProfil($id)
I have the profile function which get the data from userprofile table. and inside I have the following code for saved data:
$authed = User::find($id);
$savedarticles = $authed->mysaves;
$allsavings = DB::select("Select * from article where id=$savedarticles->id");
But this code does not work like this anyway. I can do this instead:
$authed = User::find($id);
$savedarticles = $authed->mysaves;
But when I try to get articles from article table with the article_id of mysaves, it does not work such as this:
$allsaved= DB::table('article')->where('id', $savedarticles->article_id);
the error it gives is like:
Property [article_id] does not exist on this collection instance.
although savearticle table has article_id I can output it without the line above and in view I get them as:
#foreach($savedarticles as $savedarticle)
<p>{{$savedarticle}}</p>
#endforeach
it gives me everything that is in the savearticle table and I can get do savedarticle->article_id and get article_id but can't get it in controller.
I am using Laravel 5.4.
The error message Property [article_id] does not exist on this collection instance. means you are trying to get an attribute of a single instance but from a collection.
For example the collection could be like
[$article1, $article2, $article3]
therefore what you tried to do is something similar to
[$article1, $article2, $article3]->article_id
You are trying to get an attribute from a collection instead of a single instance.
As for your query, you can use where in sql statement to search for rows that match any item in an array
$allsaved= DB::table('article')->whereIn('id', $savedarticles->pluck('article_id')->all());
What I have understood is that A USER has many POSTS and a POST belong to an article.
If this is true then you have to do following.
1: In USER model define a relation to get all posts. like below.
public function posts() {
// Foreign key will be a key that is stored in posts table and represent the user MAY BE: user_id
$this->hasMany(Posts::class, 'foreign_key', 'local_key')
}
This will allow you to get all posts belong to a user.
2: In posts, model defines a user relation like below.
public function user() {
$this->belongsTo(User::class, 'foreign_key', 'local_key');
}
This will allow you to get a post User;
3: Now in your controller you will have something like this.
public function show($user_id) {
// find a user with posts as eager loading(to avoid query again)
$user = User::with(['posts'])->where('id', $user_id)->first();
// get all posts that belong to this user
$posts = $user->posts;
}
In controller show($user_id) method you will have a user data as well as user posts data. Now if you want to get a post relations then simply define as below. let say a post belongs to an article as well.
4: In posts, model defines a relation to get an article.
public function article() {
// This will allow you to get a post artcle
$this->belongsTo(Article::class, 'foreign_key', 'local_key');
}
Now you can get the article as well while finding a user. please see below. I am rewriting controller show action to give you a better understanding.
5: Get a user with user_id
public function show($user_id) {
// find a user with posts as eager loading(to avoid query again)
// eager loading for posts & post child, this will give you NOSQL at runtime and all data will come from one query.
$user = User::with(['posts', 'posts.article'])->where('id', $user_id)->first();
// get all posts that belong to this user
$posts = $user->posts;
foreach($posts as $post) {
$article = $post->article; // Child relation of post.
}
}
Hope you will understand the flow, you have to make sure models relation to work it perfectly. If you need further help please let me know.
I am trying to figure out the correct way to implement a polymorphic relationship in Laravel using the example case of allowing the same "Comment" model to have a relationship with both "Posts" and "Videos".
This is the same example as in the Laravel docs, as follows:
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
commentable_id - integer
commentable_type - string
I have the models set up with the relationships back and forth - no issue there. My question is how to make most efficient use of the CommentsController? I would like to be able to use the same CommentsController#store method to store any type of comment, whether it be for a post or a video.
This, rather than the alternative of having a CommentsController#storePostComment and a CommentsController#storeVideoComment.
I have my routes set up as follows:
Route::post('/posts/{post}/comments', 'CommentsController#store');
Route::post('/videos/{video}/comments', 'CommentsController#store');
I have my CommentsController#store method set up as follows, currently for Posts only:
class CommentsController extends Controller
{
public function store(Post $post)
{
$post->addComment(request('body'));
return back();
}
}
Laravel's route model binding grabs the correct Post and the addComment() method saves the comment.
My question is how can I modify this to accept either a Post or a Video to the same method? I am sure this is the correct way to do it rather than creating separate methods for each, but not sure how to go about it.
Try Using SEGMENT
public function store($data) //{post} or {video} comes here
{
if(Request::segment(1) == 'posts'){
$post = new Post;
$post->addComment(request('body'));
}else if(Request::segment(1) == 'videos'){
$video = new Video;
$video->addComment(request('url'));
}
return back();
}
I have a Lesson model with below fields :
lesson_id
title
start_date
end_date
And a Content model that have these fields :
content_id
lesson_id
contentable_id
contentable_type
order
A OneToMany relationship is between Lesson and Content.
In addition, I have two another models named Unit with these fields :
unit_id
title
time
And Test by these :
test_id
title
description
Content contentable_type and contentable_id attributes holds unit (or test) data related to a specific Lesson.
For example in contentable_type field of Content can insert only App\Unit or App\Test string and contentable_id holds ID of that Unit or Test.
(Be careful that Content Model is not to create a morph relations between these tables and I created it beacause I want to give Ordering capability to units and test of a Lesson)
Now, Suppose I want to delete a specific Content and related Units (or Tests).
Deleting Content model instance is easy, but to remove related Unit (or Test), I must fetch appropriate model name from contentable_type and it's ID then select and delete it.
For that I wrote this :
public function destroy ($course_id, $lesson_id, $content_id)
{
$content = Content::findOrFail($content_id);
$modelName = $content->contentable_type;
$modelName::find($content->contentable_id)->delete();
$content->delete();
}
Despite working properly ,But I think that is not convenient.
I search for a way that when delete a Content model , this automatically found related model and remove it too.
What is best and Proper solution?
In this case I just do these changes :
public function destroy ($course_id, $lesson_id, $content_id)
{
$content = Content::findOrFail($content_id);
$content->delete();
}
And in the Content Model :
public static function boot()
{
static::deleting(function ($content) {
$modelName = $content->contentable_type;
$modelName::find($content->contentable_id)->delete();
});
}
This works fine.
But do this best and Most correct solution?