get only newest object from a model - laravel eloquent - laravel

I have two models: Supplier and Quotation. I can use a foreach to list all quotations of a supplier:
// in my `Supplier` model:
public function quotations()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id');
}
And now I want to print only the most recent quotation. The best I can figure out is this function in model:
public function newestQuotation()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id')->orderBy('offered_at','desc')->limit(1);
}
and this foreach loop:
#foreach($o->newestQuotation as $q)
#include('displaymodules.module_quotations_mini', array('bg_color' => 'bg-danger'))
#endforeach
TO DO:
The above code does the job... but how it can be done properly? I am thinking of doing a helper, but doing the function in my models seems more like a good practice.
Thank you
Edited:
maybe I wasn't clear enough... I don't want to use foreach to access the object.
What I want is a snippet like this:
#include('displaymodules.module_quotations_mini', array( 'q' => $o->latestQuotations))
I get such an object when the query has the ->first(), but not when it has '->get()`.
when I do
$latestQuotation = App\Models\Quotation:where('supplier_id', 3)->orderBy('offered_at, 'desc')->first();
to get what I want without foreach I use this this temporary solution
<?php
$latestQuotation = App\Models\Quotation::where('supplier_id', $o->id)->orderBy('offered_at', 'desc')->first();
?>
#if(isset($latestQuotation))
#include('displaymodules.module_quotations_mini', array('bg_color' => 'csch_subtle_3', 'q' => $latestQuotation))
#endif
Attempts to implement Krishan Koenig's answer
attempt #1
<?php $latestQuotations = $o->latestQuotations(); ?>
{{print_r($latestQuotations)}}
Result: domain.dev is currently unable to handle this request.
HTTP ERROR 500
attempt #2
{{$o->latestQuotations()->offered_at}}
or
{{$o->latestQuotations->offered_at}}
Result:
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$offered_at (View:\resources\views\leads\_show_bestoffers.blade.php) (View: \resources\views\leads\_show_bestoffers.blade.php)
attempt #3 - my desired solution
#include('displaymodules.module_quotations_mini', array('bg_color' => 'csch_subtle_5', 'q' => $o->latestQuotations))
Result:
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$offered_at (View:\resources\views\leads\_show_bestoffers.blade.php) (View: \resources\views\leads\_show_bestoffers.blade.php)

You could create a new scope in related model, call it like latest:
public function scopeLatest($query)
{
return $query->orderBy('offered_at','desc')->limit(1);
}
and then in your relationship:
public function newestQuotation()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id')->latest();
}

For example:
Quotation.php model
class Quotation extends Model
{
protected $primaryKey = 'id';
function withSupplier() {
return $this->hasMany('App\Models\Quotation', 'id', 'supplier_id')->orderBy('offered_at','desc');
}
public function newestQuotation($supplier_id){
News::with('withSupplier')->where('supplier_id', $supplier_id)->get();
}
}
in contriller:
public function pageQuotations(Quotation $quotation)
{
$quotations = $quotation->newestQuotation(1); //id Supplier
return view('displaymodules.quotations', $quotations);
}
in quotations.blade.php (example)
<table>
<tbody>
#foreach ($quotations as $quotation)
<tr>
<th>{{ $quotation->title }}</th>
<td>{{ $quotation->withSupplier()->title }}</td>
</tr>
#endforeach
</tbody>
</table>
</div>

You can create a new relatiotion in Supplier Model as:
public function newestQuotation()
{
return $this->hasOne('App\Models\Quotation', 'supplier_id')->orderBy('offered_at', 'desc');
}
Then you can query it as:
$supplieres = Supplier::with('newestQuotation')->take(20)->get();
#foreach ($supplieres as $supplier)
{{ $supplier->newestQuotation->title }} // just an example
#endforeach
Update
Try your query as:
$supplier = Supplier::where('id', $id)->first();
$latestQuotation = $supplier->newestQuotation;

Create a scope and use your existing relationship!
public function quotations()
{
return $this->hasMany('App\Models\Quotation', 'supplier_id');
}
public function scopeLatest($query)
{
return $query->orderBy('offered_at','desc')->limit(1);
}
public function latestQuotations()
{
return $this->quotations()->latest();
}
And in your Controller you query them with
//...
$latestQuotations = $supplier->latestQuotations();
Edited Version:
// supplier model
public function quotations()
{
return $this->hasMany(Quotation::class);
}
public function scopeLatest($query)
{
return $query->orderBy('offered_at','desc');
}
public function latestQuotations()
{
return $this->quotations()->latest()->get();
}
// web route
Route::get('/', function () {
$latestQuotations = App\Supplier::first()->latestQuotations();
return view('welcome', compact('latestQuotations'));
});
// welcome view
{{ $latestQuotations }}
...worked for me.

The most simple solution is
$entity = $collection->first();
Then, whateverthe count of the collection, the result is a single object.
This allows to use terms such as $entity->id or $entity->name.

Related

How to re-render a table with Livewire after an event emitted from another component

I'm new to Livewire and I am stuck with this problem.
I've created a table.blade.php component with livewire, and another searchbar.blade.php component, which is not a child of the table component. Every time a search for a term, the table should rerender with the seached parameter.
All is right, and the search query gives the correct result (clients with pagination), but somehow the table does not rerender the html.
Any ideas what I'm doing wrong? Thanks
<div>
<input type="text" wire:model="query" autofocus>
</div>
class SearchBar extends Component
{
public $query;
public function updatedQuery()
{
$this->emit('searchForQuotes', $this->query);
}
public function render()
{
return view('livewire.clients.searchbar');
}
}
<div>
<table>
<tbody>
#foreach($clients as $client)
#livewire('clients.row', ['client' => $client], key($client->id))
#endforeach
</tbody>
</table>
</div>
class Table extends Component
{
use WithPagination;
public $query;
protected $listeners = [
'searchForQuotes' => 'render'
];
public function mount()
{
$this->resetData();
}
public function resetData()
{
$this->query = null;
}
public function render($query = null)
{
$q = Client::query();
if ($query) {
$q->whereRaw("CONCAT(surname, ' ', name) LIKE '%" . $query . "%'");
}
$clients = $q->latest()->paginate(20);
return view('livewire.clients.inc.table', [
'clients' => $clients, 'query' => $query
]);
}
}
You can make your child components reactive by making your key() unique every render of the parent:
#livewire('clients.row', ['client' => $client], key($client->id . "-" . Str::random()))
By adding a Str::random(), the key is different every time the parent updates, which forces the children to update as well. This also works with now(), but only as long as you have a prefix. It is important to note that this causes more requests and thus can make your table slower.
Try something like this :
class Table extends Component
{
use WithPagination;
public $query;
protected $listeners = ['searchForQuotes'];
public function mount()
{
$this->resetData();
}
public function searchForQuotes($query)
{
$this->query = $query;
// Do something
$this->render();
}
public function resetData()
{
$this->query = null;
}
public function render()
{
$q = Client::query();
if ($this->query) {
$q->whereRaw("CONCAT(surname, ' ', name) LIKE '%" . $query . "%'");
}
$clients = $q->latest()->paginate(20);
return view('livewire.clients.inc.table', [
'clients' => $clients, 'query' => $this->query
]);
}
}
I think I found the problem, but don't know how to solve it.
I the table.blade.php component I've got this code.
#foreach($clients as $client)
#livewire('clients.row', ['client' => $client], key($client->id))
#endforeach
It seems like the nested component are not rendering after firing the event.

Differentiate two views with buttons

I have a view in which I display two data that I take from a table of a DB and next to these data I have two buttons that take me to the same view in which I need to display other things.
What I can't figure out is, how do I differentiate the view AFTER pressing the button?
Here there are the images of the two view:
https://imgur.com/a/i5BcPaw
The code is the following:
Controller:
<?php
namespace App\Http\Controllers;
use App\Models\Device;
use App\Models\DataFromRasp;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
class DeviceController extends Controller
{
public function index()
{
$data=Device::all();
return view('backend.auth.user.device', compact("data"));
}
public function create()
{
}
public function store(Request $request)
{
}
public function show(Device $deviceID)
{
}
public function edit(Device $device)
{
//
}
public function update(Request $request, Device $device)
{
//
}
public function destroy(Device $device)
{
//
}
/**
* Displays all data present in the table data_from_rasps
*
* The data are all the devices that the raspberry can capture
*/
public function visualizeData()
{
$data=DataFromRasp::paginate(10);
return view('backend.auth.user.dictionary', compact("data"));
}
/**
* Raspberry capture and send the data to the DB and save in another
* table of the same DB the MAC addresses of interest
*/
public function getData(Request $request)
{
$m_data = $request->get('m_data');
$r_data = $request->get('r_data');
DataFromRasp::create(['MAC' => $m_data, 'RSSI' => $r_data]);
if(($m_data == 'C4:A5:DF:24:05:7E') and Device::where('MAC_ADDR', $request->m_data)->doesntExist()){
Device::create(['USERNAME'=>'Device1','MAC_ADDR' => $m_data]);
}
if(($m_data == '70:1C:E7:E4:71:DA') and Device::where('MAC_ADDR', $request->m_data)->doesntExist()){
Device::create(['USERNAME' => 'Device2','MAC_ADDR' => $m_data]);
}
}
public function scan()
{
$process = new Process(['C:\Simone\Università\Tirocinio\laravel-boilerplate-master', 'prova.py']);
$process->run();
if (!$process->isSuccessful()) { throw new ProcessFailedException($process); }
return redirect()->route('dict');
}
public function singleDev(Device $deviceID){
$data = DataFromRasp::select('RSSI', 'created_at')->where('MAC', 'C4:A5:DF:24:05:7E')->get();
$time_array = [];
$rssi_array = [];
$cnt = 0;
foreach($data as $dataItem){
array_push($time_array, $dataItem->created_at->format('H:i:s'));
array_push($rssi_array, $dataItem->RSSI);
}
return view('backend.auth.user.singleDevice', compact("time_array", "rssi_array"));
}
}
View where I select the device:
#extends('backend.layouts.app')
#section('content')
<table class="table">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">USERNAME</th>
<th scope="col">MAC ADDRESS</th>
</tr>
</thead>
<tbody>
#foreach ($data as $item)
<tr>
<th scope="row">{{$item->id}}</th>
<td>{{$item->USERNAME}}</td>
<td>{{$item->MAC_ADDR}}</td>
<td>
Select
</td>
</tr>
#endforeach
</tbody>
</table>
#endsection
Does anyone know how to do this?
Thanks in advance
Try to use this code
Select
Instead of
Select
Updated!
I a little bit didn't get what you trying to achieve, but why didnt your try to add another button next to it, to show another view?
You could create an if statement to determine if one of the buttons is pressed whenever you load the page.
In blade
<button type="submit" name="device_id" value="$item->id">Select</button>
In controller
$data=Device::all();
$singleDevice = Device::where('id', '=', Input::get('device_id'))->first();
if($singleDevice === null)
{
return view('backend.auth.user.device', compact("data"));
}
else
{
$time_array = [];
$rssi_array = [];
$cnt = 0;
foreach($singleDevice as $dataItem){
array_push($time_array, $dataItem->created_at->format('H:i:s'));
array_push($rssi_array, $dataItem->RSSI);
}
return view('backend.auth.user.singleDevice', compact("time_array", "rssi_array"));
}

Laravel 7 Search Model

I am trying to build a search engine but I am coming up blank with how to build the search. I have about 5 input fields for the user to search (heading , usersGroup , course ,title(question type). I am test one code ,and its work just for search according the title,but for other search it dose not work ,what should I do??
public function show_page_search()
{
$headings = heading::all();
$usersGroup = usersGroup::all();
$courses = course::all();
$questionType = $this->questionType;
return view('Admin.Page.Question.search', compact(['headings', 'usersGroup', 'courses', 'questionType']));
}
public function get_data_search(Request $request)
{
$questions = question::Where('title', 'LIKE', "%{$request->title}%")->get();
if ($request->type !== null)
$questions = $questions->where('type', $request->type)->all();
Its my Model :
class question extends Model{
protected $table = 'questions';
protected $guarded = ['id'];
public function headings()
{
return $this->belongsToMany(heading::class, 'question_headings', 'question_id', 'heading_id');
}
public function userGroup()
{
return $this->belongsToMany(usersGroup::class, 'question_users_groups', 'question_id', 'users_groups_id');
}
public function opt()
{
return $this->hasMany(questionOpt::class, 'question_id');
}
public function answer()
{
return $this->hasMany(answerQuestion::class, 'question_id');
}
it is my view :
<tbody class="text-center">
#forelse(session()->get('questions') as $question)
<tr>
<th scope="row"><span
class="badge badge-success p-2">{{ $loop->iteration }}</span></th>
<td>
#foreach($question->headings as $heading)
<span>{{$heading->title}}, </span>
#endforeach
</td>
<td>
#foreach($question->userGroup as $userGroup)
<span>{{$userGroup->name}}, </span>
#endforeach
</td>
<td>{{get_question_type_name($question->type)}}</td>
<td>{{$question->title}}</td>
<td>
get and all returns the result as a collection. So before calling any method that would return the result, you would build the complete query.
So change this:
public function get_data_search(Request $request)
{
$questions = question::Where('title', 'LIKE', "%{$request->title}%")->get(); //remove this get()
if ($request->type !== null)
$questions = $questions->where('type', $request->type)->all(); //remove this all()
To this:
public function get_data_search(Request $request)
{
$questions = Question::where('title', 'LIKE', "%{$request->title}%");
if ($request->type !== null)
$questions->where('type', $request->type); //use `like` if you want
if (another condition)
$questions->where('column', 'value');
//and finally call get() or all() or paginate() to get the final result
$questions->get();
return view('view', compact('questions'));
}

Loop through nested relation in view

I'm trying to loop through some data in my blade view. This is my database setup:
What I would like to have in my view is the following:
So I can loop through the days and then order the tasks by the category they are in. I'm not experienced with Laravel and I'm having difficulties on ordering the tasks by category in the best way.
In my Days Model I have:
public function tasks()
{
return $this->belongsToMany('App\Task');
}
In my Task Model I have:
public function days()
{
return $this->belongsToMany('App\Day');
}
public function category()
{
return $this->belongsTo('App\Category');
}
In my Category Model I have:
public function tasks()
{
return $this->hasMany('App\Task');
}
In my controller I now only have:
public function index()
{
$categories = Category::all();
return view('app.blade.php', compact('categories'));
}
But how can I make sure to order them by days and then by category? Help much appreciated.
You can try as following:
$days = Days::with('tasks.category')->get();
$days = $days->map(function ($day) {
$day->tasks = $day->tasks->groupBy('category_id');
return $day;
});
Check it how it is converted after groupBy() method
dd($days);
then in your view you can do as:
#foreach ($days as $day)
{{ $day->name }}
#foreach ($days->tasks as $tasks)
{{ $tasks->first()->category->name }}
#foreach ($tasks as $task)
{{ $task->name }}
#endforeach
#endforeach
#endforeach
Note - I have not tested it but you can give it a try.

how to acces nested objects in blade templates

Let me show you what I have.
//Post Controller
public function showPost(Post $post)
{
$albums = $post->album()->with('photos')->get();
$comments = $post->comments()->where('approved', '=', 1)->get();
$this->layout->title = $post->title;
$this->layout->main = View::make('home')->nest('content','posts.single', compact('post', 'comments','albums'));
}
mysql sentence executed correctly
string 'select * from albums where albums.id = '{"id":19,"title":"Post no 19","read_more":"Lorem i'... (length=786)
string 'select * from albums where albums.post_id = '19'' (length=54)
string 'select * from images where images.album_id in ('4')' (length=57)
I want to access the Photo object in blade, everything parses to the template but when I try to get the Photos
#foreach($albums->Photos as $photo)
Undefined property: Illuminate\Database\Eloquent\Collection::$Photos (View:
// post model
public function album()
{
return $this->hasOne('Album');
}
//album model
public function Photos(){
return $this->hasMany('Image');
}
Try
#foreach($albums->Photos()->get() as $photo)
{{$photo->attribute}}
#endforeach
You need to call the function that holds the relationship then use
get()
method to return an Eloquent Collection and then iterate it with a foreach loop.
#foreach($albums as $album)
#foreach($album->Photos as $photo)
<p>{{ $photo->image }}</p>
#endforeach
#endforeach
THats the answer

Resources