Laravel 4 Redirect::action() "Route not defined" - php

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.

Related

Yii Missing attribute when using join() + one()

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.

Querying MorphToMany Relation Laravel

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)

reduce database query to one and avoid Call to a member function load() on null error

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.

Laravel Redirect URL Controller

I want redirect url from /topic/{title} to /topic/{category}/{title}.
So I try to registered this in routes:
Route::get('/topic/{title}',function(){
return redirect()->action('/topic/{category}/{title}','DetailController#index');
});
But I got error
Action App\Http\Controllers/topic/{category}/{title} not defined.
Anyone can help me about the routes?
Thanks for advance.
This is my Controller
class DetailController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index($section=0,$title = 0)
{
$detail = DB::table('t_artikel')
->join('t_section', 't_section.id_section', '=', 't_artikel.id_section')
//->join('t_kolom', 't_kolom.id', '=', 't_artikel.penalar')
->where('publish', '=', 'Y')
->where('parent_id', '=', 0)
->where('tgl_pub', '<=', date('Y-m-d H:i:s'))
->where('t_artikel.urltitle', '=', $title)
->select('t_artikel.*', 't_section.*', 't_artikel.urltitle as urlartikel')
->first();
$kategori = DB::table('t_section')
->where('id_supsection', '=', 0)
->where('status', '=', 1)
->orderBy('position', 'asc')
->whereNotIn('id_section', [34])
->select('t_section.*')
->get();
$page = 'Detail';
return view ('detail.detail',
['detail' => $detail,
'kategori' => $kategori,
'page' => $page
]);
}
As per the documentation,
If your controller route requires parameters, you may pass them as the second argument to the action method
So, you need to pass the parameters like this:
return redirect()->action(
'DetailController#index', ['category' => <enter_value>, 'title' => <enter_value>]
);
I would suggest you to use 2 routes and 2 controllers. One handles the details of the topic and the other one handles the redirect of the "old" url.
For example: the user will visit "/topic/title" that is handled by a controller that will recognize topic and category, then will use the
public function handleTopic($title){
// the code here will recognize topic and category
// and will just provide the stuff to show the right page
// in the end will redirect to that controller
return redirect()->action('DetailController#index', ['category' => $category, 'title' => $title]);
}
public function index($stuffYouNeed){
// retrieve the rest of data you need to show the page
// you already know the title and category (cause of previous controller)
// in the end return the view with data
return view ('detail.detail',['data' => $data]);
}
In your routes you'll have to add one route and edit the existing one like:
Route::get('topic/{title}', 'DetailController#handleTopic')->name('handleTopic');
Route::get('topic/{category}/{title}', 'DetailController#index')->name('showTopic');
It's not tested cause atm i don't have a laravel env set up in local. But i think it should work. Let me know
Edit: I forgot to explain why you see the error
Action App\Http\Controllers/topic/{category}/{title} not defined.
You are using the redirect incorrectly
Route::get('/topic/{title}',function(){
return redirect()->action('/topic/{category}/{title}','DetailController#index');
});
You can only provide an action controller, not a route. And the destination route have to exist. So the right use is:
return redirect()->action('Controller#action');
Anyway, you should split logic from routes. You have controllers for that... even for really short blocks of code. It will keep everything in order and clear. In your route file you should have ONLY routes.
Solved, I found my way after modified. and this is the code:
Route::get('/topik/{title}',function($title){
$article = DB::table('t_artikel')
->join('t_section', 't_section.id_section', '=', 't_artikel.id_section')
->where('publish', '=', 'Y')
->where('parent_id', '=', 0)
->where('tgl_pub', '<=', date('Y-m-d H:i:s'))
->where('t_artikel.urltitle', '=', $title)
->select('t_artikel.*', 't_section.urltitle as urltitlesec', 't_artikel.urltitle as urlartikel')
->first();
$kategori = $article->urltitlesec;
return redirect("/topik/{$kategori}/{$title}");
});
If you want to redirect to a URI, then don’t use redirect()->action(); use redirect()->to() instead:
Route::get('/topic/{title}', function ($title) {
$category = ''; // Get category some how
return redirect()->to("/topic/{$category}/{$title}");
});

Laravel Several Eager Loading with constraints

I am having an issue regarding Eager Loading with constraints. I have a method that returns all projects.
public function findProjectById($id, $user_id)
{
$project = Project::with([
'tasklists' => function($query) {
$query->with(['tasks' => function($q) {
$q->with(['subtasks', 'comments' => function($com) {
$com->with('user');
}]);
}])
->with('project');
}
])->findOrFail($id);
return $project;
}
This works fine. But i want to return only those projects where a task belongs to a current user. There is a field in tasks table for user(user_id). For that i used a where query inside tasks.
$query->with(['tasks' => function($q) {
$q->where('user_id', '=', $user_id);
$q->with(['subtasks', 'comments' => function($com) {
$com->with('user');
}]);
}])
But it throws Error Exception "Undefined variable user_id"
"type":"ErrorException","message":"Undefined variable: user_id"
Can anyone point out what seems to be the problem. Thanks.

Categories