simplify this #foreach/if in blade and laravel - php

I am fairly new to laravel (L5 specifically) and I am making my own version of a todo app rather than following one of the tutorials out there. I've learned quite a bit so far but the way I have this piece of code currently laid out in my blade template makes me think their might be a simpler way of doing this.
My TodosController#index fn is
public function index()
{
$todos = Todo::get();
return view('todos', compact('todos'));
}
App\Todo extends an Eloquent model which makes data handling very easy!
My route is:
Route::bind('todos', function($slug)
{
return App\Todo::whereSlug($slug)->first();
});
So my page simply displays an unorded list of "todos". I want to have two separate lists. One that is for completed todos and one for incomplete. My blade template looks like this so far and looks a bit messy. Also I am looping over the results twice which is where I think I can improve on.
<h3>Incomplete</h3>
<ul>
#foreach ($todos as $todo)
#if ($todo->completed == 'No')
<li>
{{ $todo->title }}
</li>
#endif
#endforeach
</ul>
<h3>Complete</h3>
<ul>
#foreach ($todos as $todo)
#if ($todo->completed == 'Yes')
<li>
{{ $todo->title }}
</li>
#endif
#endforeach
</ul>
Any suggestions to simplify that blade template?

DRY your code out. You could streamline it by moving the actual item mark-up to a partial template since it’s repeated in both the complete and incomplete lists:
<h3>Incomplete</h3>
<ul>
#foreach ($todos as $todo)
#if ($todo->completed == 'No')
#include('partials.items.todo')
#endif
#endforeach
</ul>
<h3>Complete</h3>
<ul>
#foreach ($todos as $todo)
#if ($todo->completed == 'Yes')
#include('partials.items.todo')
#endif
#endforeach
</ul>
And partials.items.todo would look like this:
<li>
{{ $todo->title }}
</li>
I would also re-factor your loops. Instead of looping over the same list twice, you could split them in your controller:
public function index()
{
$todos = Todo::where('user_id', '=', Auth::id())->get();
$complete = $todos->filter(function ($item) {
return $item->completed = 'Yes';
});
$incomplete = $todos->filter(function ($item) {
return $item->completed = 'No';
});
return view('todos', compact('complete', 'incomplete'));
}
Looking at your Todo model, I’d also make your completed column in the database a boolean field instead of a column containing “Yes” or “No” strings. You could then cast that column value to a proper boolean (since MySQL doesn’t have a native boolean field type):
class Todo extends Model
{
protected $casts = [
'completed' => 'boolean',
];
public function isComplete()
{
return $this->completed;
}
}
And then re-factor your controller action to use this instead:
public function index()
{
$todos = Todo::where('user_id', '=', Auth::id())->get();
$complete = $todos->filter(function ($item) {
return $item->isComplete() === true;
});
$incomplete = $todos->filter(function ($item) {
return $item->isComplete() === false;
});
return view('todos', compact('complete', 'incomplete'));
}
You could even move those collection filters to a custom TodoCollection class:
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
class TodoCollection extends EloquentCollection
{
public function complete()
{
return $this->filter(function ($item) {
return $item->isComplete() === true;
});
}
public function incomplete()
{
return $this->filter(function ($item) {
return $item->isComplete() === false;
});
}
}
Sorry for the lengthy reply, but should give you food for though on how to re-factor your code.

Only a bit simplified but...
You can try in your controller:
public function index()
{
$completed = Todo::where('completed','Yes')->get();
$incompleted = Todo::where('completed','No')->get();
return view('todos', compact('completed', 'incompleted'));
}
in Your template:
<h3>Incomplete</h3>
<ul>
#foreach ($incompleted as $todo)
<li>
{{ $todo->title }}
</li>
#endforeach
</ul>
<h3>Complete</h3>
<ul>
#foreach ($completed as $todo)
<li>
{{ $todo->title }}
</li>
#endforeach
</ul>
Another approach using a subtemplate like this:
//_list_todos.blade.php
#foreach ($todos as $todo)
<li>
{{ $todo->title }}
</li>
#endforeach
And your main template like this:
<h3>Incomplete</h3>
<ul>
#include('_list_todos',['todos'=>$incompleted] )
</ul>
<h3>Complete</h3>
<ul>
#include('_list_todos',['todos'=>$completed] )
</ul>
The advantege to use a subtemplate like the last one is you can reuse the code, and simplify your main templates.

Related

pagination error - laravel Method Illuminate\Database\Eloquent\Collection::paginate does not exist

public function seguimientos()
{
// $tracks = Tracking::with(array('order' => function($query)
// {
// $query->where('user_id',Auth::user()->id );
//
// }))->get();
if(!Auth::user()) return abort(404);
if(!Auth::user()->type == 'admin' || !Auth::user()->type == 'empresa') return abort(404);
// $trackings = Tracking::all();
$trackings = Tracking::with('order')->get()->paginate(15);
$myCompany = Auth::user()->company;
$statuses = ['Pago exitoso', 'Espera de envio','Pedido en Camino','Pedido Entregado'];
return view('store.order.admin.seguimiento')->with(['trackings'=>$trackings,'statuses'=>$statuses,'myCompany'=>$myCompany]);
}
I try to pag, but I can't get it to work
blade.php
<div style="margin-top: -44px;" class="page">
<ul class="list-group">
#foreach($trackings as $tracking)
#if($tracking->order->user->company == $myCompany )
<li class="list-group-item"> <b>Orden N°:</b> {{ $tracking->order->id }} <b>Detalles</b></li>
</ul>
#endif
#endforeach
{{ $trackings->$links() }}
<hr>
</div>
error: Method Illuminate\Database\Eloquent\Collection::paginate does not exist.
in: $trackings = Tracking::with('order')->get()->paginate(15);
help pls
You can't use paginate after get so you want to change your code like below.
$trackings = Tracking::with('order')->paginate(15);

Troubles with getting sub-categories of main category in Laravel

I'm trying to learn Laravel while writing some common features. Now what I'm trying to make is when I click on main category link to open new page and display all sub-categories assigned to this category. Sounds pretty simple but I can't display them.
This is what I have in my Category Model
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
And in controller
public function categoryListing( $category_id )
{
$categories = Category::with('children')->get();
$category = Category::find($category_id);
if($category->parent_id == 0) {
$ids = Category::select('id')->where('parent_id', '!=',0)->get();
$array = array();
foreach ($ids as $id) {
$array[] = (int) $id->id;
}
} else {
$items = Item::where('category_id' ,$category_id)->paginate(5);
}
return view('category_list', compact('categories','items'));
}
The idea here is to display Main Category and all sub-categories (childs) of this main category.
And this is the loop on the page
#foreach($categories as $category)
<a href="{!!route('list',array($category->id))!!}">
<span><strong>{!!$category->title!!}</strong> ({!! $category->itemCount!!})</span>
</a>
<ul class="list-group">
#foreach($category as $subcategory)
{!!$subcategory->title!!}
<span class="badge badge-primary badge-pill">{!! $subcategory->itemCount !!}</span>
</li>
#endforeach
</ul>
#endforeach
Current error is
Trying to get property of non-object
on the inside foreach.
Just try this is 'cleaner'.. if it works, start adding your links and other content so you will know what does not work.
#foreach($categories as $category)
<span><strong>{!!$category->title!!}</strong></span>
<ul class="list-group">
#foreach($category as $subcategory)
<li>{!!$subcategory->title!!}</li>
#endforeach
</ul>
#endforeach
Make sure the variables you try to print exist in each object.
You can try to dd($categories); in your controller before the return view('category_list', compact('categories','items')); statement to see whats inside $categories

How to retrieve data from multiple tables in Laravel 5.4

I have two tables and i would like to retrieve data from them and pass it to my table.
For this I've created 2 models with an one to one relationship:
[Adress]
class Adress extends Model
{
public function KontoKorrent()
{
return $this->hasOne(KontoKorrent::class, 'Adresse');
}
}
[KontoKorrent]
class KontoKorrent extends Model
{
public function Adresse()
{
return $this->belongsTo(Adress::class,'Adresse');
}
}
My controller look like this:
class AdressesController extends Controller
{
public function index()
{
$adresses = Adress::with('KontoKorrent')->paginate(2);
return view('welcome', compact('adresses'));
}
}
When I use tinker
App\Adress::
Every adress has relation to the kontokorrent. This is working.
App\Adress {#698
Adresse: "3030",
Anrede: "Company",
Name1: "A Company Name",
LieferStrasse: "Dummystreet",
KontoKorrent: App\KontoKorrent {#704
Location: "1",
Adresse: "3030",
Kto: "S0043722",
In my view:
<ul>
#foreach($adresses as $adress)
<li>{{ $adress->Name1 }}</li> //this is working
<li>{{ $adress->KontoKorrent->Kto }}</li> //this is NOT working
#endforeach
</ul>
{{ $adresses->links() }}
The relation is showing me an error:
Trying to get property of non-object
What I'm doing wrong ?
The error that you are getting:
Trying to get property of non-object
Is related to some Adress model that doesn't have a KontoKorrent, then your $adress->KontoKorrent returns null, and null isn't a object, that the reason of the message.
To fix it, you should do an if to check if adress have the relationship:
<ul>
#foreach($adresses as $adress)
<li>{{ $adress->Name1 }}</li> //this is working
<li>
#if($adress->KontoKorrent)
{{ $adress->KontoKorrent->Kto }}
#else
<!-- Put something here if you want, otherwise remove the #else -->
#endif
</li> //this is NOT working
#endforeach
</ul>
This can be shortened to:
{{ $adress->KontoKorrent ? $adress->KontoKorrent : 'the else content' }}
or in PHP >= 7.0, you can use the null coalesce operator:
{{ $adress->KontoKorrent ?? 'the else content' }}

Laravel 5.2 correct way to use variables in blade

So I know about passing variables via the controller for instance if its a query array I will do
public function index()
{
$query = Request::get('q');
if ($query) {
$users = User::where('username', 'LIKE', "%$query%")->get();
}
return view('view', compact('users'));
}
And when on the blade I will do
#if( ! empty($users))
#foreach($users as $user)
{{ $user->username }}
#endforeach
#endif
Now my question is how do I set a variable using a variable from the foreach? at the moment I am using PHP inside of the blade template file but I feel this is messy, here is what I have
#if( ! empty($users))
#foreach($users as $user)
<?php
$lastOnline = \Carbon\Carbon::createFromTimeStamp(strtotime($user->last_online))->diffForHumans();
$fiveMinsAgo = \Carbon\Carbon::now()->subMinute(5);
?>
{{ $user->username }}
#if ($user->last_online <= $fiveMinsAgo)
{{ $lastOnline }}
#else
Online Now
#endif
#endforeach
#endif
found a solution to my issue if anyone else is ever looking for it.
public function getLastOnlineAttribute($value)
{
$fiveMinsAgo = \Carbon\Carbon::now()->subMinute(5);
$thirtMinsAgo = \Carbon\Carbon::now()->subMinute(30);
$lastOnline = \Carbon\Carbon::createFromTimeStamp(strtotime($value))->diffForHumans();
if ($value <= $fiveMinsAgo) {
echo 'Last Active: '.$lastOnline.'';
}
else {
echo 'Online Now';
}
}
Basically add this into your model for the variable (eg, if its a $user->last_online it would go into the user model) , it is called a eloquent mutator if you are ever looking for more info, https://laravel.com/docs/master/eloquent-mutators
It grabs your data for the variable for instance {{ $user->last_online }}
Note that the Underscore is transformed into a CamelCase in the function name, the output is set at $value, you can then set variables inside of the function and mould the output however you wish, then in the blade you can get rid of all the extra crap and just use {{ $user->last_online }}

"Use of undefined constant..." error in Laravel

I am a Laravel newbie. I want to pass the results of a database query to a view. I get an error message "Use of undefined constant tasks - assumed 'tasks'". What am I doing wrong?
My code is as follows:
class TasksController extends BaseController{
public function index(){
$tasks = Task::all();
//return View::make(tasks.index, ['tasks' => $tasks]);
return View::make(tasks.index, compact('tasks'));
}
A snippet from my template page is shown below:
<body>
<h1>All tasks!</h1>
#foreach($tasks as $task)
<li>{{ $task-title }} </li>
#endforeach
return View::make('tasks.index')->with(compact('tasks'));
also change:
<li>{{ $task-title }} </li>
to
<li>{{ $task->title }} </li>
should be like this.
Try this,
return View::make(tasks.index, $tasks);
instead of
return View::make(tasks.index, compact('tasks'));

Categories