Laravel - Use a column from a model relation - php

in my livewire view i get all the credits from their respective owners through the model relationship between clients and credits
Model: Client
protected $fillable = ['name'];
public function credits(){
return $this->hasMany(Credit::class);
}
public function payments(){
return $this->hasManyThrough(Payment::class, Credit::class);
}
Model: Credit
const ACTIVE= 1;
const LIQUIDATED= 2;
protected $fillable = ['amount', 'amount_end', 'status', 'dues'];
public function client(){
return $this->belongsTo(Client::class);
}
public function provider(){
return $this->belongsTo(Provider::class);
}
public function payments(){
return $this->hasMany(Payment::class);
}
in my livewire view I have a foreach that gets me all the clients, inside a switch that counts the credits of the clients
Livewire: Show-client
#foreach ($clients as $item)
...
#switch($item->credits->count())
#case($status_credit = 1)
<span class="px-2 py-1 inline-flex text-sm leading-5 font-semibold rounded-md bg-yellow-300 text-yellow-600 geosanslgbold text-md">
Credit
</span>
#break
#case($status_credits >= 2)
<span class="px-2 py-1 inline-flex text-sm leading-5 font-semibold rounded-md bg-orange-300 text-orange-700 geosanslgbold text-md">
Credits
</span>
#break
#default
<span class="px-2 py-1 inline-flex text-sm leading-5 font-semibold rounded-md bg-green-400 text-green-900 geosanslgbold text-md">
Not Credits
</span>
#endswitch
#endforeach
So far everything is fine, it tells me the credits and tells me if it has or not.
now the credits have the option to place them as active or liquidated
How would I make my switch, instead of telling me all the client's credits, only take the active or in this case the ones with the number 1 and add the active credits
sorry for my bad english

I believe you are looking for what's called "local query scopes" : https://laravel.com/docs/9.x/eloquent#local-scopes
public function scopeIsLiquidated(Builder $query): Builder
{
return $query->where('status', LIQUIDATED);
}
Usage :
$clients = ...->isLiquidated()->...;

Related

Laravel Eloquent Query within Foreach Loop

Back again with another question I am hoping someone might have an idea for regarding the app that I am working on.
In this educational assessment application, I have assessments which have a one-to-many relationship with Competencies (similar to a Blog Post Category) and a many-to-many relationship with Contexts (similar to a Blog Tag).
I am trying to create a report view which will allow me to show a card for each of the Competency & Context combinations and then count all assessments where that competency & context combination exists (think of this as counting how many blog posts are in each category & tag combination, even if that number is 0).
So far I am able to produce the report which lists a card for each competency and context combination but I can't figure out how to pass that information to the controller for use in the query which will find the relevant assessments.
Here is my Report View
<x-app-layout title="{{ config('app.name', 'Laravel') }}">
<div class="container grid px-6 mx-auto">
<h2 class="my-6 text-2xl font-semibold text-gray-700 dark:text-gray-200">
{{ __('Reports') }}
</h2>
<div class="grid gap-6 mb-8 md:grid-cols-2 xl:grid-cols-4">
#foreach ($competencies as $competency) <br>
#foreach ($contexts as $context)
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200">
{{ $competency->name}}
</p>
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
{{ $context->name }}
</p>
{{ $assessments }}
</div>
</div>
#endforeach
#endforeach
</div>
</x-app-layout>
Here is my Report Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Assessment;
use App\Models\Competency;
use App\Models\Context;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
class ReportController extends Controller
{
public function index(Competency $competency, Context $context)
{
return view('dashboard.reports.index', [
'competencies' => Competency::where('team_id', Auth::user()->currentTeam->id)->get(),
'contexts' => Context::where('team_id', Auth::user()->currentTeam->id)->get(),
'assessments' => Assessment::where('competency_id', $competency->id)->whereRelation('contexts', 'context_id', $context->id)->count(),
]);
}
}
Here is an image of what this currently produced in the Report View: https://imgur.com/a/SQSf7UM
Please let me know if there is additional detail which would be helpful
it's better you declare the relation in the model, let's assume:
an assessment has one category,
an assessment has many contexts,
You can create an additional table with its model to store assessment contexts (because it assessent can has many contexts), let's say the table name is assessment_contexts and the model name is AssesmentContext. Or you can just run php artisan make:model AssessmentContext -m.
At least it has 2 columns, assessment_id and context_id,
Then inside AssessmentContext, add this function to create a simple relation,
public function assessment() {
return $this->hasOne(Assessment::class);
}
public function context() {
return $this->hasOne(Context::class);
}
Declare this functions inside the Assessment model,
public function competency() {
return $this->hasOne(Competency::class);
}
public function contexts() {
return $this->hasMany(AssesmentContext::class);
}
And add the this to the Competency model,
public function assessments() {
return $this->hasMany(Assessment::class);
}
And this for the Context model,
public function assessments() {
return $this->hasMany(AssessmentContext::class);
}
You have to add foreign key inside assessments table that refers to category primary key id, (I recommend you the column name is category_id).
Then finally in your controller, you can just declare the competencies list, and inside your view, you can access all the relate data. i.e:
return view('dashboard.reports.index', [
'competencies' => Competency::where('team_id', Auth::user()->currentTeam->id)->get()
]);
Inside view,
#foreach ($competencies as $competency) <br>
#foreach ($competency->assessments as $assessment)
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200">
{{ $competency->name}}
</p>
#foreach ($assessment->contexts as $context)
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
{{ $context->name }}
</p>
#endforeach
</div>
</div>
#endforeach
#endforeach
This is just simple scheme of relation, there are other ways to do it that maybe better and more optimal.

Stuck on search function for multiple parameters

I have a page with multiple posts, each post has a location and department & title.
I implemented the search function for "word search" and it works fine, but in addition to this I have two Dropdown menus for location & department, they get their content from two different table.
I am kind of stuck and don't know why it doesn't work, here is my controller code:
public function index(Request $request)
{
$posts = Post::orderBy('titel')->get();
$standorts = Standort::orderBy('standort_name')->get();
$abteilungs = Abteilung::orderBy('abteilung_name')->get();
if ($request->filled('s') || $request->has('standort') || $request->has('abteilung')) {
$word = strtolower($request->get('s'));
$location = $request->standort;
$dep = $request->abteilung;
$query = strtolower($request->get('s'));
$posts = $posts->filter(function ($post) use ($dep, $word, $location) {
if (
Str::contains(strtolower($post->Titel), $word) ||
Post::where('standort_name') == $location
|| Post::where('abteilung_name') == $dep
) {
return true;
}
return false;
});
}
return view('posts.overview', [
'posts' => $posts,
'standorts' => $standorts,
'abteilungs' => $abteilungs,
]);
}
Here is my blade code:
<form class="mb-5 flex justify-left grid grid-cols-4 gap-x-20" action=" {{ route('overview') }}"
method="get">
<div class="w-full h-12 text-left col-start-1 col-end-1">
<input placeholder="Schlagwort" type="text" id="s" name="s" value="{{ request()->get('s') }}"
class="mb-2 w-full h-full bg-white bg-opacity-95 rounded focus:ring-2 border border-gray-300 focus:border-indigo-500 text-base outline-none text-gray-700 text-xl py-1 px-3 leading-8">
</div>
<div class="text-left mb-4 h-12">
<select name="abteilung" id="abteilung" class="h-full w-full flex justify-center bg-white bg-opacity-95 rounded focus:ring-2 border border-gray-300 focus:border-indigo-500 text-base outline-none text-gray-700 text-lg text-center leading-8">
<option selected="true" disabled="disabled">Abteilung</option>
#foreach($abteilungs as $abteilung)
<option value="{{ $abteilung->abteilung_name }}">{{ $abteilung->abteilung_name }}</option>
#endforeach
</select>
</div>
<div class="text-left mb-4 h-12">
<select name="standort" id="standort" class="h-full w-full flex justify-center bg-white bg-opacity-95 rounded focus:ring-2 border border-gray-300 focus:border-indigo-500 text-base outline-none text-gray-700 text-lg text-center leading-8">
<option selected="true" disabled="disabled">Standort</option>
#foreach($standorts as $standort)
<option value="{{ $standort->standort_name }}">{{ $standort->standort_name }}</option>
#endforeach
</select>
</div>
<button class="col-start-4 col-end-4 w-11/12 h-12 text-white text-2xl px-4 py-2 rounded text-base font-medium bg-gradient-to-r from-green-400 to-blue-500 float-right shadow transition duration-500 ease-in-out transform hover:-translate-y-1 hover:scale-100">
Suchen
</button>
</form>
When I dd this part:
$word = strtolower($request->get('s'));
$location = $request->standort;
$dep = $request->abteilung;
I get the correct attributes, for instance a word like "HR", a location like "Munich" and a department like "Finance".
When I perform the search though for a specific department all posts are gone.
Edit for clarity:
The goal is that a user can search for a word in a title, a location and department all in one request, and only the post where each is present gets shown.
Edit Relation Models:
Post Model:
class Post extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'titel',
'startdate',
'enddate',
'beschreibung',
'standort',
'type_name',
'abteilung_name',
'kontakt',
'isActive',
'lebenslauf',
'zeugnisse',
'anschreiben',
'weitere_Doks',
'is_Permitted',
'job_start',
];
public function location() {
return $this->hasOne(Standort::class);
}
public function job_type() {
return $this->hasOne(Jobtypes::class);
}
}
Abteilung Model:
class Abteilung extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'abteilung_name',
];
public function posts() {
return $this->hasMany(Post::class);
}
}
Standort Model:
class Standort extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'abteilung_name',
];
public function posts() {
return $this->hasMany(Post::class);
}
}

Laravel Relationship: Trying to get property 'description' of non-object

I'm facing against this problem since yesterday.
I have a db table called resources have a foreign key linked to another table called category.
I'm trying to retrieve the description field in my blade view, but I get this error:
Trying to get property 'description' of non-object.
My blade view:
#extends('templates.header')
#section('section')
<div class="p-10 grid grid-cols-1 sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5">
#foreach($resources as $resource)
<div class="max-w-sm rounded overflow-hidden shadow-lg">
{{-- <img class="w-full" src="#" alt="Mountain"> --}}
<div class="px-6 py-4">
<div class="font-bold text-xl mb-2">
{{ $resource->name }}
</div>
<p class="text-gray-700 text-base">
{{ $resource->description }}
</p>
</div>
<div class="px-6 pt-4 pb-2">
<span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">{{ $resource->categor->description }}</span>
<span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">{{ $resource->status }}</span>
<span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">{{ $resource->centerId }}</span>
<button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Prenota
</button>
</div>
</div>
#endforeach
</div>
#endsection
My Resource Model:
namespace App\Models;
use App\Models\Category;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Resource extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'description',
'category',
'inventoryN',
'status',
'centerId',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
];
public function category()
{
return $this->hasOne(Category::class, 'id', 'category');
}
}
My Category Model:
namespace App\Models;
use App\Models\Resource;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
protected $table = 'categories';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'description',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
];
public function resource()
{
return $this->belongsTo(Resource::class, 'category');
}
}
And lastly my ResourceController:
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Resource;
use Illuminate\Http\Request;
class ResourceController extends Controller
{
public function index()
{
$resources = Resource::with('category')->get();
return view('resources', compact('resources'));
}
}
This is a dd of "$resources":
dd of $resources
You have a few mistakes here.
The first is in the Blade. You need to fix a typo
$resource->categor->description
// should be
$resource->category->description
Then I recommend changing your schema by changing your resources column from category to category_id.
This will help Laravel auto populate the values in the below snippets.
Next, you need to fix your relationships.
In the Resources model, you need
public function category()
{
return $this->hasOne(Category::class);
}
I have removed the second and third parameters, these are autofilled by Laravel; and since you are using Laravel's naming schemes, you don't need it.
What you had previously was stating that the table was the singular variant of category, which it wasn't.
Then you need to change your Category model to
public function resource()
{
return $this->belongsTo(Resource::class);
}
The reason this was failing is because Laravel was returning null, because the column names weren't quite correct.
It's easier to just have a more standard naming structure in your Database as it helps other developers, and makes your life easier when using Laravel.
There is a typo in your category viewing. I think that's the problem.
{{ $resource->categor->description }}
vs.
{{ $resource->category->description }}

Laravel Livewire wire:click not trigger function defined

I am building a search bar within a webpage. Ideally, user would enter the search text in the search field, and then if there are records found, a search results table would show and display the record found. I am using Laravel Livewire to implement this feature, however, I ran into the problem that the wire:click not firing the event, and any help would be needed!
This is my blade file (resources/livewire/dashboard.blade.php) contains the search bar:
<form>
<label for="searchText" class="block text-xx font-medium text-gray-700">Search Users</label>
<div class="mt-1 flex rounded-md shadow-sm">
<div class="relative flex items-stretch flex-grow focus-within:z-10">
<input type="text" name="searchText" id="searchText"
class="focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300" placeholder="User ID / Email Address / Mobile Number"
wire:model="searchText">
</div>
<button wire:click="search()" class="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 text-sm font-medium rounded-r-md text-gray-700 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500">
Search
</button>
</div>
</form>
and this is the action defined in the App/Http/Livewire/Dashboard.php file
<?php
namespace App\Http\Livewire;
use Illuminate\Support\Facades\Http;
use Livewire\Component;
class Dashboard extends Component
{
public $stats, $searchText;
public $showResultsTable = false;
protected $accountAPIRootURL = 'https://example.com/api/v2/';
public function render()
{
$response = Http::withHeaders([
'Accept' => 'application/json'
])->get($this->accountAPIRootURL . 'statistics/overview');
if ($response->successful()) {
$stats = $response['data'];
} else {
$stats = [
'total_users' => 0,
'new_users' => 0,
'invitations' => 0,
'new_invitations' => 0,
'requests' => 0,
'new_requests' => 0
];
}
$this->stats = $stats;
$this->searchText = '';
return view('livewire.dashboard');
}
public function search()
{
$response = Http::withHeaders([
'Accept' => 'application'
])->get($this->accountAPIRootURL . 'admin/search', [
'searchText' => $this->searchText
]);
if ($response->successful()) {
$this->showResultsTable = true;
$this->searchText = '';
}
}
}
This is my template.blade.php file, where the #livewire component is called
#extends('layouts.app')
#section('content')
#livewire('dashboard')
#endsection
I am not worrying too much about displaying the result table now because it seems like the search() function is not being triggered when I click on the Search button within the blade. How do I know that, I put a dd() within the search() function and it is not being executed.
I would appreciate any help!
You don't need to use the parenthesis, wire:click="search"
UPDATE: Try this different syntax while you are handle a form in livewire
<form wire:submit.prevent="search">
//.....
<div class="mt-1 flex rounded-md shadow-sm">
//.....
<button class="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 text-sm font-medium rounded-r-md text-gray-700 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500">
Search
</button>
</div>
</form>

Livewire/Alpine Dom template not updating

first time using Livewire with Alpine and its a real pain to debug. Alpine console errors are so vague, is there anyway to make them more specific and verbose?
I digress.
I'm updating an array on livewire component which, when not empty, should be showing in the DOM. Everything works and when viewed in the console i can see that the changes are being made. inside the console everything is happening as it should. The trouble is, nothing i happening in the browser!
<div x-data="{ ...data() }" class="overflow-hidden wrapper w-full ">
<div class="flex justify-end w-full relative coins-container space-x-6">
<input id="search-toggle" type="search" pclass="block w-full bg-gray-100 focus:outline-none focus:bg-white focus:shadow text-gray-700 font-bold rounded-lg pl-12 pr-4 py-4 shadow-xl" wire:model.debounce.750ms="searched_term" />
</div>
#if($filtered_variable)
<template>
<div class="mt-1 wrapper">
<div id="search-content" class=" w-full text text-gray-600 rounded-lg overflow-y-auto bg-white shadow-xl" style="max-height: 500px;">
<div id="searchresults" class="h-auto w-full mx-auto">
#foreach ($filtered_variable as $index => $value)
<h1 x-text="{{$value['title']}}"></h1>
#endforeach
</div>
</div>
</div>
</template>
#endif
</div>
Class Searchbar extends Component
{
public $first_array;
public $searched_term;
public $filtered_array;
public function mount()
{
$db_content = Stuff::where(function ($query) {
$query->where('thing', false)
->orWhereNull('thing');
})
->with('variable_eg')
->get();
$this->first_array = $db_content;
$this->searched_term = '';
$this->filtered_array = [];
}
public function render()
{
return view('livewire.searchbar');
}
public function updated($name, $value)
{
if (empty($this->searched_term)) {
return '';
}
$this->filtered_array = array_filter($this->first_array, array($this, 'filter'));
public function filter($element)
{
$title = strtolower($element['title']);
if (strpos($title, $this->searched_term) !== false) {
return true;
}
}
}
inside the console I can see that my alpine/livewire component is receiving the filtered_value just as expected. But nothing is happening on the browser. How can I force rerender?
To update (refresh/rerender) your component you can use a listener
protected $listeners = ['refreshComponent' => '$refresh'];
Then when the refreshComponent event is emitted, it will refresh the component without running any other actions.
There is a Github discussion in the Livewire repo describing this.
Regarding your updating and filter methods (just as a hint):
There are also special methods like updatingFoo, updatedFoo that run before/after an item foo is updating. See the Lifecycle Hooks.

Categories