Problemes in retrieving completed courses - laravel

I'm new on Laravel 6 and build an application for students. The tables of my database look as follows:
$courses
-program_id
-title
$projects
-user_id
-course_id
-title
-completed
-comments
I have two users with different rights (Admin and Member). The Admin user can decide if a project is completed or not (checkbox). Each project belongs to a course (course_id).
I have a success.blade.php which looks as follows:
#extends('layouts/app')
#section('content')
<div class="row justify-content-center">
<div class="jumbotron col-8">
<h4 class="display-4">{{ __('Finished Projects') }}</h4>
<ol>
#foreach(Auth::user()->projects as $project)
#if($project->comments)
#if($project->completed === 1)
<li>{{ $project->title }}</li>
#endif
#endif
#endforeach
</ol>
{{ __('Back')}}
</div>
</div>
#endsection
This file retrieves completed projects, but I would like to retrieve the courses of the completed projects instead. How can I solve this?
My Controller method looks like this:
public function success()
{
$programs = Program::orderBy('name')->get();
$courses = Course::orderBy('title')->get();
$projects = Project::orderBy('title')->get();
return view('/success', [
'programs' => $programs,
'courses' => $courses,
'projects' => $projects
]);
}

you can access to desire course with this query for each project that is completed:
$course = Course::find($project['course_id']);

Using relationships
https://laravel.com/docs/7.x/eloquent-relationships
// From controller you can do
$courses = Course::whereHas('project', function($q){
$q->where('completed', true);
})->get();
// Also this way
$completedProjects = Project::where('completed', true)->get();
// Blade view
#foreach($completedProjects as $project)
#foreach($project->courses as $course)
{{ $course->title }}
#endforeach
#endforeach

Related

I want to displaying comments for each post

hello I just moved from php native and want to try relationships in laravel, so I want to display a post and each comment and the name of user who comments so that when looped the post has a comment that has a relationship with it.
So this is my HomeController.php :
public function index() {
$posts = Post::all();
$comment = Comment::all();
return view('home', [
'posts' => $posts,
'comments' => $comment
]);
}
My Post.php :
public function comments() {
return $this->hasMany(Comment::class);
}
public function user() {
return $this->belongsTo(User::class);
}
My Comments.php :
public function post() {
return $this->belongsTo(Post::class);
}
public function user() {
return $this->belongsTo(User::class);
}
My User.php :
public function post() {
return $this->hasMany(Post::class);
}
My home.blade.php :
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($comments as $comment)
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
the code above runs with the post displayed along with the author but all the comments also appear in all posts, I have searched for references and tried to change the comments value in Controller but the results are still the same so I made the comments appear in each post *$comment = Comment::all();.
what I want is to display posts and comments that relate to each post, like a twitter feature that can reply to people's tweets.
Thx you..
Your relations builded well, so just can simply use $post->comments can get all comments related on each post.
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($post->comments as $comment) // make changes here
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
public function index() {
$posts = Post::with('comments')->get();
return view('home', [
'posts' => $posts,
]);
}
then in blade
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($post->comments as $comment)
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
You have done fine job building models and their relationship.
I recommend you change how you call post query.
public function index(Request $request) {
// add eager loading comments for each post and user
$posts = Post::with('comments', 'user')->get();
// eager loading user is needed, cause in blade you call $post->user->name
// calling comments will no longer needed
// return only $posts
return view('home', compact('posts'));
}
And inside your blade, you can call comments from each post, like this.
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($post->comments as $comment)
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
That's it and if you use barryvdh/laravel-debugbar , you will see there are only 3 queries run, each one from table posts, comments and users.
And for comments query and users query, not all rows will be fetched,
only related to fetched posts from the 1st query.
Have fun using Laravel Framework!
Your PHP native knowledge/expertise will be very useful and speed up your learning/mastering process.

Show category posts in Laravel is not working

I want to show posts that are in a category by clicking on them, but when I click on a category,$articles in category.blade.php returns null.
$articles should return articles that have a specific category
this is my index.blade.php:
<div class="tab-pane fade" id="show-categories" role="tabpanel">
<h6 class="sidebar-title">Categories</h6>
<div class="row link-color-default fs-14 lh-24">
#foreach($categories as $category)
<div class="col-6">
<a href="{{ route('cms.category', $category->id) }}">
{{ $category->name }}
</a>
</div>
#endforeach
</div>
</div>
category.blade.php:
#extends('layouts.app')
#section('content')
#forelse ($articles as $article)
<h1>{{$article->title}}</h1>
#empty
<span>array is empty</span>
#endforelse
#endsection
router:
Route::get('cms/categories/{category}', [articlesController::class, 'category'])->name('cms.category');
and articlesController:
public function category(Category $category)
{
return view('cms.category')
->with('category', $category)
->with('articles', $category->articles()->searched()->simplePaginate(3))
->with('categories', Category::all())
->with('tags', Tag::all());
}
Please, don't forget to define the relationship from Category to Article on your model. Like: one category has many articles. For example:
On your Category.php model:
public function articles()
{
return $this->hasMany(Article::class);
}
Then on your, controller, you may call the relationship like this way:
public function category(Category $category)
{
$articles = Category::with("articles")->where("id", $category->id)->simplePaginate(3);
return view('cms.category')
->with('category', $category)
->with('articles', $articles)
->with('categories', Category::all())
->with('tags', Tag::all());
}
Please share the model file and second I prefer to write the category method in a different way.
public function category($category_id)
{
try {
$articles= Article::where('id', $category_id)->get();
return view('cms.category', $articles);
} catch (\Throwable $th) {
Log::error($th->getMessage());
}
}
Let me know if you still face any issue. Try to use dd $articles ..

Laravel 5.7 | Pagination - eloquent query (relationship table)

how can I use pagination, where my eloquent query looks:
PageController - method show:
$page = Page::where('slug', $slug)->with(['subpages'=>function($q) {
$q->where('visible', 1)->orderBy('order', 'asc')->paginate(9);
}])->first();
I would like to paginate subpages on page's View blade (Page - one to many - Subpage relationship)
Here is part of my show.blade.php view:
{{ $page->subpages->links() }}
Error:
Method Illuminate\Database\Eloquent\Collection::links does not exist.
EDIT:
FULL FUNCTION SHOW:
public function show(PageRepository $pageRepo, $slug){
$page = Page::where('slug', $slug)->with(['subpages'=>function($q) {
$q->where('visible', 1)->orderBy('order', 'asc');
}])->first();
$subpages = $page->subpages()->paginate(2);
$sidebar = Navbar::where('type','sidebar')->orderBy('order')->with(['pages'])->get();
return view('pages.page.show', [
"page" => $page,
"subpages" => $subpages,
"sidebar" => $sidebar,
]);
}
VIEW:
<div class="row display-flex">
#foreach($page->subpages as $subpage)
<div class="col-sm-4">
<div class="card mb20">
<div class="card-block">
<h4 class="card-title font400">{{ $subpage->title }}</h4>
</div>
</div>
</div>
#endforeach
</div>
{{ $subpages->links() }}
If your goal is to paginate the relation from what I see, but you're querying the parent model. That's what's wrong. You have to completely change your logic since the subquery doesn't map the relation inside the Laravel Model. It's just used as a condition to retrieve that model.
You query should look like this:
$page = Page::where('slug', $slug)->with(['subpages'=>function($q) {
$q->where('visible', 1)->orderBy('order', 'asc') // ->paginate(9); This is not necessary
}])->first();
Now that you have the parent model, you can execute a new query for the relation, and there you have to call the paginate method:
I don't think that would be a good practice to do something like this in your blade view:
{{ $page->subpages()->paginate(9)->links() }}
But it's better to implement a new variable:
// Previous code that retrieves the page
$subpages = $page->subpages()->paginate(9);
return response()->view('your.blade.view', compact('page', 'subpages');
now you can use the subpages variable, that contains the paginated results, in your view

Use "where" and "limit" to child in #foreach

I want to display all user's profile into views and they posts. It's pretty simple:
#foreach($profiles as $profile)
{{ $profile->name }}
#foreach($profile->posts as $post)
{{$post->title}}
#endforeach
#endforeach
But I want to display only the latests posts (->orderBy('created_at', 'desc')->limit(4) ) and only accepted posts (->where('accept', 1)). How can I do that?
You already have what you need. You just need to put it all together.
#foreach($profile->posts()->where('accept', 1)->orderBy('created_at', 'desc')->limit(4)->get() as $post)
I would consider creating a query scope on your profile model. That way you can do something like $profile->latestPosts.
Or you can use the relationship to return exactly what you need.
public function latestPosts() {
return $this->posts()->where('accept', 1)->orderBy('created_at', 'desc')->limit(4)->get();
}
Then you could use it as:
#foreach($profile->latestPosts() as $post)
{{$post->title}}
#endforeach
A better solution would be to lazy load the posts you need when loading the profiles in the controller.
$profile = Profile::with(['posts' => function ($post) {
$post->where('accept', 1)->orderBy('created_at', 'desc')->limit(4);
}])->limit(10)->get();
then in the blade you can do the same as before
#foreach($profiles as $profile)
{{ $profile->name }}
#foreach($profile->posts as $post)
{{$post->title}}
#endforeach
#endforeach
if you want to use a scope for latestAccepted, you can on the Post model
class Post extends Model
{
public function scopeLatestAccepted($query)
{
return $query->where('accept', 1)->orderBy('created_at', 'desc')->limit(4)
}
}
Then lazy load it
$profile = Profile::with(['posts' => function ($post) {
$post->latestAccepted();
}])->limit(10)->get();
what you have to do is pretty simple.
create table users
create table post
create a relationships between the 2 tables
write you code in controller to join the table and query all the users post based on his/her username or password
see sample screenshot for table relationships
//Controller
Public function ViewPost(){
$data['data']=DB::table('useratable')
->where('useratable.username','=', $uname)
->where('useratable.password','<=',$pwd)
->leftjoin('posttable', 'useratable.idusertable', '=', 'posttable.userid')
->orderby('posttable.idposttable','desc')
->get();
}
//Route
Route::get('/view-post','YourController#ViewPost')
//view
#foreach($data as $r)
{{ $r->fullname}} <br>
{{ $r->email}} <br>
{{ $r->topic }} <br>
{{ $r->content }} <br>
{{ $r->datetime}} <br>
...
#endforeach
You need some codes like it on controller:
$profiles = Profile::with(['posts' => function ($query) {
$query->where('accept', 1)
->orderBy('created_at', 'desc');
}])->get();
And add this one to blade file:
#foreach($profiles as $profile)
{{ $profile->name }}
#foreach($profile->posts as $post)
{{$post->title}}
#if ($loop->iteration == 4)
#break
#endif
#endforeach
#endforeach

Call to a member function tasks() on null on laravel 5

Let me explain with a couple of word my problem.
On my controller i have this line:
$tasks = $student->group->tasks()->orderBy('created_at', 'desc')->withPivot('id')->get();
This works for existing users, but when i try to create new ones i receive
Call to a member function tasks() on null
Can i with something like this or what do you suggest ?
if(!is_null($tasks))
$tasks = $student->group->tasks()->orderBy('created_at', 'desc')->withPivot('id')->get();
}
This i my show function on controller
public function show(){
$user = Auth::user();
if(!$user)
return redirect('/')->withErrors(config('constants.NA'));
$countries = Country::all()->lists('name', 'id')->toArray();
$profile = $user->profile;
$student = $profile->student;
// Tasks showed on students profile
if ($student->group) {
$tasks = $student->group->tasks()
->orderBy('created_at', 'desc')
->withPivot('id')
->get();
}
// Classmates
if ($student->group) {
$classmates = $student->group->students()
->where('id', '!=', $student->id)
->get();
}
// Activated books
$books = Auth::user()->books()->orderBy('grade_id', 'asc')->get();
if(!is_null($student->group))
$iTasks = $student->group->tasks->count();
else {
$iTasks = 0;
}
$iTodos = $user->todos->count();
return view('student.profile.show',compact('user','profile', 'countries', 'iTasks', 'iTodos', 'tasks', 'classmates', 'books'));
}
This is my show view, for the tasks
<div class="tab-pane <?php if(isset($tab) && $tab == 'timeline'): ?> active <?php endif; ?>" id="timeline">
#if($tasks->count())
<div class="timeline">
#foreach($tasks as $task)
<!-- TIMELINE ITEM -->
<div class="timeline-item">
<div class="timeline-badge">
<div class="timeline-icon">
<i class="mdi mdi-clipboard-text font-red-intense"></i>
</div>
</div>
<div class="timeline-body">
<div class="timeline-body-arrow"> </div>
<div class="timeline-body-head">
<div class="timeline-body-head-caption">
<span class="timeline-body-title font-blue-madison">
{{ $task->professor->profile->user->name }}
</span>
<span class="timeline-body-time font-grey-cascade">ju ca caktuar një detyrë të re.</span>
</div>
</div>
<div class="timeline-body-content">
<span class="font-grey-cascade">
{{ $task->pivot->comment }}
</span>
</div>
<hr>
Lenda: <span class="timeline-body-time font-grey-cascade sbold">{{ $task->subject->name }}</span>
<div class="pull-right">
Krijuar më: <span class="timeline-body-time font-grey-cascade sbold">{{ $task->created_at->format(Config::get('klasaime.date_format')) }}</span>
</div>
</div>
</div>
<!-- END TIMELINE ITEM -->
#endforeach
</div>
#else
<div class="alert">
Ju nuk keni asnje detyrë të caktuar!
</div>
#endif
</div>
Looks to me like you haven't added the student to a group yet, or that somehow hasn't been persisted. If you have added the student to a group after creating and saving the student, try this:
$student->load('group')
Before running:
$tasks = $student->group->tasks()->orderBy('created_at', 'desc')->withPivot('id')->get();
I'll need to see more of your code to give a more accurate answer. But the error you're getting is related to the ->group, of your student, not your student itself.
Do a simple check to see if the group exists and then carry on with whatever action you need to perform.
// controller
$tasks = null;
if ($student->group) {
$tasks = $student->group->tasks()
->orderBy('created_at', 'desc')
->withPivot('id')
->get();
}
// view
#if($tasks)
{{ $tasks->id }}
#else
No tasks found
#endif
Edit : In your controller add $tasks = null; and $classmates = null; at top. And in your view change #if($tasks->count()) to #if($tasks). I'm not sure where your use the classmates variable, but add a check to see if it's null before you use it.

Resources