I don't know if this is correct behavior of Yii Active Record, consider I have this code
$post = Post::find()
->alias('p')
->select(['p.*', 'COUNT(c.id) AS comment_count'])
->join('LEFT JOIN', 'comments c', 'p.id = c.post_id')
->groupBy('p.id')
->one();
I cannot access $post->comment_count, but when I use ->asArray()->one, I can access $post['comment_count'], is it possible to return as Post model while having access to comment_count? As this can be used for validation, example
// $post from code above
if ($post->comment_count != 0) {
throw new UnprocessableEntityHttpException('Cannot delete post with comment(s)');
}
return $post->delete();
You need to add $comment_count inside the Post model, for example:
class Post {
public $comment_count;
.....
public function attributeLabels()
{
return [
'comment_count' => 'Total Comment',
.....
]
}
But if you are satisfied with asArray() as what you mentioned earlier, I think that is enough because it's pretty faster.
Related
So I'm a total newbie in laravel and I don't know if it can be done but I saw that in the controller I can display data of a specific 'id' with this in my api.php:
Route::get('books/{id}', 'App\Http\Controllers\BooksController#getBookById');
And this in my BookController.php :
public function getBookByAuthor($id) {
$book = Books::find($id);
if (is_null($book)){
return response()->json(['message' => 'Book Not Found.'], 404);
}
return response()->json($book::find($id), 200);
}
I'm using Angular for the front and I have a searchbar to search by 'title' of a book, so in my database I have a column 'title' and I want to fetch data by 'title' instead of 'id'.
Is it possible ? And if yes how ?
I'm thinking you're wanting to retrieve the book based on user input...? You can inject the request in your method. Also you don't need to explicitly handle 404 and response codes.
use Illuminate\Http\Request;
use App\Models\Book;
public function getBookByAuthor(Request $request): Response
{
$input = $request->validate([
'title' => 'required|alpha_dash' // validate
]);
return Book::where('title', 'like', "%{$input['title']}%")
->firstOrFail();
}
Validation docs
The thing I want to get from the database is that get all the posts with the data which will identify whether the post is liked by the auth()->user() or not. Most probably via count.
App\Post
public function likes()
{
return $this->morphToMany('App\User', 'likeable');
}
App\User
public function likePosts()
{
return $this->morphedByMany('App\Post', 'likeable')->withTimestamps();
}
Likeables Table
Likeables table has ('user_id', 'likeable_id', 'likeable_type')
I tried using orWhereHas
$posts = Post::with( ['user', 'tags', 'category'])->orwhereHas('likes', function($q) {
$q->where('user_id', auth()->id());
})->latest()->withoutTrashed()->paginate(10);
But with about query I am only getting those posts which the user has liked. I want to get all posts and a check whether the post is liked by the user or not
I came across whereHasMorph but it was only for morphTo and not for morphToMany.
#m____ilk I was able to solve this but creating a mutator:
public function isLiked()
{
return $this->likes()->where('user_id', auth()->id())->count() > 0;
}
I ran a loop on the posts and attached a custom attribute to a single post based on the mutator.
$posts = Post::with( ['user', 'tags', 'category', 'post.user', 'post.tags', 'post.category'])->latest()->withoutTrashed()->paginate(10);
foreach ($posts as $post) {
// Mutator Condition
if ($post->is_liked) {
// Custom Attribute
$post->isLiked = 1;
} else {
$post->isLiked = 0;
}
}
return $posts;
In laravel 9 I did something like:
$posts = Post::with( ['user', 'tags', 'category', 'post.user', 'post.tags', 'post.category'])
->withCount([
'likes',
'likes as is_liked' => function($q) {
$q->where('user_id', auth()->id());
}
])->latest()->withoutTrashed()->paginate(10)
I have this function:
public function show($id)
{
if (count($post = Post::find($id))) {
$post = $post->load(['comments' => function ($q) {
$q->latest();
$q->with(['author' => function ($q) {
$q->select('id', 'username');
}]);
}, 'user' => function ($q) {
$q->select('id', 'username');
}]);
$this->authorize('seePost', $post);
return view('post.show', ['post' => $post]);
} else {
dd('no post');
}
}
I added the if statement as if I try to open a route to a non existent post id I get the error Call to a member function load() on null.
However now I have two queries, one looks for the Post in the DB and if it finds one then I have to load the relations with the second one. What can I do to go back to just one query with all the relations loaded and avoid the error? Any clue?
You can use Constraining Eager Loads do it like this:
https://laravel.com/docs/5.8/eloquent-relationships#constraining-eager-loads
$post = Post::with(["comments" => function ($query) {
// Order by created_at, query comment author & select id, username
$query->latest()->with(["author" => function ($q) {
$q->select("id", "username");
}]);
}, "user" => function ($query) {
// Query post author & select id,username
$query->select("id", "username");
}])
// Fetch post or throw a 404 if post is missing
->findOrFail($id);
// You can also return an empty post instance like this if post is missing
// ->findOrNew([]);
// Or return the post or null if post is missing
// ->find($id);
// Authorize
$this->authorize('seePost', $post);
return view("post.show", ["post" => $post]);
Laravel has an Eager Loading feature that would be helpfull in your case. Eager Loading allows you to autoload relations along with the same query that you use to retrieve your main model info. https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
You could a below codes.
Easiest way is :
$post = Post::with('comments.author', 'user')
->find($id);
Or fine tune query with callback :
$post = Post::with(['comments' => function ($q) {
// if you use comments select, then you need to specify foreign key too
$q->select('id', 'author_id', 'details') // comment fields
->latest(); // Use chaining method
// OR use $q = $q->latest();
},
'comments.author' => function ($q) {
$q->select('id', 'username'); // author fields
},
'user' => function ($) {
$q->select('id', 'username'); // user fields
}])
->find($id);
In some cases you might need some modifications, bu in overall that should avoid you N+1 queries problem.
now I have this controller which gets id from db and than list som information from db. but when I go to database and delete the data manually, the controller can't find the id anymore and it returns :Trying to get property of non-object
with planti of privet information. my conde is something like below:
public function saveChanges(Request $request){
$id=$request->input('id');
$this->validate($request, [
'title' => 'required',
'body' => 'required|min:2'
]);
$post=Post::where('id',$id)->first();
if($post->id == $id){
$post->title = $request['title'];
$post->postBody = $request['body'];
if ($post->update())
{
return response()->json([
'type'=>1,
'newtitle'=>$post->title,
'newbody'=>$post->postBody
]);
}
else{
return response()->json(['type'=>0]);
}
}
else {
echo"404";
}
}
the thing I don't like here is going for the id directly like this:
$post=Post::where('id',$id)->first();
I don't have much idea about laravel so do you think I may prevent this situation by any chance?
As suggested, check first if a post was found before trying to access a property:
$post = Post::where('id',$id)->first();
if ($post instanceof Post && $post->id == $id) {
// ...
}
For reference, see:
http://php.net/manual/en/language.operators.type.php
First you should check if object Post exists, for example by function is_object()
http://php.net/is_object
I'm currently having troubles with Laravel 4. I would like to use slugs for forum categories and forum topics (slugs are unique). In order to determinate if the user is in a category or in a topic, I have this route:
Route::get('forum/{slug}', function($slug) {
$category = ForumCategory::where('slug', '=', $slug)->first();
if (!is_null($category))
return Redirect::action('ForumCategoryController#findBySlug', array('slug' => $slug));
else {
$topic = ForumTopic::where('slug', '=', $slug)->first();
if (!is_null($topic))
return Redirect::action('ForumTopicController#findBySlug', array('slug' => $slug));
else
return 'fail';
}
});
And I have the following error when I try to reach a category:
Route [ForumCategoryController#findBySlug] not defined.
Here is my ForumCategoryController:
class ForumCategoryController extends BaseController {
public function findBySlug($slug) {
$category = ForumCategory::where('slug', '=', $slug)->first();
return View::make('forum.category', array(
'title' => 'Catégorie',
'category' => $category
));
}
}
Where is the problem ? Is there a way to do it better ? Help please :)
Laravel is telling that you have to define a route to use Route::action(), something like:
Route::get('forum/bySlug/{slug}', 'ForumTopicController#findBySlug');
Because it will actually build an url and consumme it:
http://your-box/forum/bySlug/{slug}
For that it must find a route pointing to your action.