Splitting Eloquent methods between model and controller? - laravel

I have this code in a Controller:
public function getStock(Request $request){
$laptops = new Laptop;
$laptops->addJoins();
$laptops->filterResults($request);
return $laptops->get();
}
In the laptops Model I have:
public function addJoins() {
$this->Join('locations', 'locations.id', '=', 'laptops.location_id');
$this->Join('models', 'models.id', '=', 'laptops.model_id');
$this->Join('types', 'types.id', '=', 'models.type');
$this->leftJoin('statuses', 'status.id', '=', 'laptops.status');
$this->leftJoin('earmarks', 'earmarks.laptop', '=', 'laptop.id');
}
public function filterResults($request) {
if ($request->stock) {
$this->where('locations.stock', 0);
}
if ($request->loc) {
$this->where('laptops.location_id', $request->loc);
}
}
The problem is I receive all laptop records, and the filtering from URL parameters doesn't appear to be working at all. Am I barking up the wrong tree with this structure (I'm very new to Laravel)?

The filtering isn't working because running your queries on $this will result in unwanted behavior, this is what local scopes are for. See the documentation for them here:
https://laravel.com/docs/master/eloquent#local-scopes
In your case it would be:
Your Laptop model:
public function scopeAddJoins($query)
{
return $query->join('locations', 'locations.id', '=', 'laptops.location_id')
->join('models', 'models.id', '=', 'laptops.model_id')
->join('types', 'types.id', '=', 'models.type')
->leftJoin('statuses', 'status.id', '=', 'laptops.status')
->leftJoin('earmarks', 'earmarks.laptop', '=', 'laptop.id');
}
public function scopeFilterResults($query, $request)
{
if ($request->has('stock')) {
$query->where('locations.stock', 0);
}
if ($request->has('loc')) {
$query->where('laptops.location_id', $request->input('loc'));
}
return $query;
}
Then you can do this in your Controller:
public function getStock(Request $request)
{
return Laptop::addJoins()->filterResults($request)->get();
}

Instead of reinventing the wheel I'd recommend you to change structure. Skinny controller, fat model. The good practice is to keep controllers as thin as possible, so my controller would look like this:
public function getStock(Request $request)
{
return view('laptop.index', [
'laptops' => Laptop::getAllLaptops($request->all())
]);
}
Also, use Eloquent and scopes for filtering etc. All data related logic would be in model:
public function getAllLaptops($data)
{
return $this->with('locations', 'models', 'types', 'statuses', 'earmarks')->search($data)->get();
}

My first thought is that $request->stock and $request->loc does not have any value in your URL parameters. If you are calling the route this way:
http://www.yoursite.com/yourpath?stock&loc
Instead, try to use it this way:
http://www.yoursite.com/yourpath?stock=1&loc=1
The point is to give them some value.

Related

How to get data from Laravel relation

I have 2 models: publictxt and Pulicetxtrecive
publictxt model:
class Publictxt extends Model
{
protected $fillable=['sender','reciver','description','useridseen'];
public function Publictxtrecives()
{
return $this->hasMany(Pulicetxtrecive::class,'publictxt_id', 'id');
}
}
Pulicetxtrecive model:
protected $fillable=['publictxt_id','user_id','seen'];
public function publictxts()
{
return $this->belongsTo(Publictxt::class);
}
I want to get the values ​​from the Publictxt that are available in the Pulicetxtrecive.
When a record is stored in the Publictxt, users are registered in the Pulicetxtrecive after viewing.
$pulictxtcount=Publictxt::where('reciver',Auth::user()->shift)->orwhere('reciver',1)->with('Publictxtrecives')->whereHas('Publictxtrecives',function($q){$q->where('seen', '=', 0);})->count();
this code doesn't work.
There are some conflicts in your database structure.
You said when a user sees a letter the Publictxtrecives will be created.
That means if a Publictxt has a Publictxtrecives that definitely has been seen .
But there is a seen column in Publictxtrecives table.
You should pick one.
But anyway as this structure:
$pulictxtcount=Publictxt::where(
function($query){
$query->where('reciver',Auth::user()
->shift)->orwhere('reciver',1);
})
->Where(function($query)
{
$query->whereHas('Publictxtrecives',
function($q){$q->where('seen',1);
}
)->orWhereDoesntHave('Publictxtrecives');
})
->with('Publictxtrecives');
$pulictxtcount = Publictxt::with([
'Publictxtrecives' => function($query){
$query->where('seen', 0);
}
])
->where('reciver', Auth::user()->shift)
->orwhere('reciver', 1)
->get();
I solved this problem:
$pulictxtcount = Publictxt::where(function($q){$q->where('reciver',Auth::user()->shift)->orwhere('reciver',1);})->whereDoesntHave('Publictxtrecives', function($q){$q->where('user_id', Auth::user()->id);
})->count();

Laravel Model request with several joins

While I am able to make simple requests with Model, I can't say the same for more complicated ones.
I know I don't necessarily have to use Model and can use DB facade but still, I want to know how it's supposed to be done.
Here's a request I made using DB :
DB::table('relationships')
->Join('users','users.id','=','relationships.user_id')
->Join('roles','roles.id','=','relationships.role_id')
->Join('bundles','bundles.id','=','relationships.related_id')
->Join('pools','bundles.id','=','pools.bundle_id')
->whereIn('pools.name',$pools)
->whereIn('roles.name',$roles)
->select('users.first_name','users.last_name','users.mail_address','roles.name AS role_name','bundles.name AS bundle_name', 'pools.name AS pool_name')
->get();
On a first attempt, I tried this:
User::whereHas('relationships', function($req) use($roles) {
$req->whereHas('bundle', function($req){
$req->whereIn('name', $pools);
});
$req->whereHas('role', function ($req){
$req->whereIn('name', $roles);
});
})
->with('relationships', 'relationships.role:id,name', 'relationships.bundle:id,name')
->get();
}
Problem is, using "with" just select everything unconditionally, ignoring previous tests you made earlier (whereHas, whereIn).
So I'd have to again filter on each table in the with statement.
Then I ended up doing this:
$pools = request()->input('pools.*.name');
return $prepReq = User::whereHas('relationships', function($req) use($pools, $roles) {
$req->whereHas('bundle', function($req) use ($pools){
$req->whereHas('pools', function($req) use ($pools){
$req->whereIn('name', $pools);
});
});
$req->whereHas('role', function ($req){
$req->whereIn('name', $roles);
});
})
->with(['relationships' => function ($query) use($pools, $roles){
$query->whereHas('role', function ($query){
$query->whereIn('name', $roles)
->select('id','name');
})->select('id','name');
}])
->get(['id', 'first_name', 'last_name', 'mail_address']);
Then I got lost into this and gave up.
Another thing that made me sweat is that when you go nested using "with", you can select columns only on the last table.
For example: "relations.bundle.pools" => I can select columns on pools but not on relationships or bundles, does that mean i have to imbricate with statements for each table ?
As you can see, I am a bit clueless on how things are supposed to be done
I would like any advice or help regarding this matter
Thanks in advance for your time
When using Laravel, you should be setting up the eloquent relationships for each model
Based on your select statement from above, and assuming you want to get the user, bundle, role, and pool, then I would do the following, may be off depending on how your actual DB and models are set up
// Relationship.php
public function user()
{
return $this->belongsTo(User::class);
}
public function role()
{
return $this->belongsTo(Role::class);
}
public function bundle()
{
return $this->belongsTo(Bundle::class, 'id', 'related_id');
}
// Bundle.php
public function relationship()
{
return $this->hasOne(Relationship::class, 'related_id');
}
// Role.php
public function relationship()
{
return $this->hasOne(Relationship::class);
}
// Pool.php
public function bundle()
{
return $this->belongsTo(Bundle::class);
}
Then you could do something like
Pool::with('bundle.relationship.user')->whereIn('name', $pools);
Role::with('relationship.user')->whereIn('name', $roles);

Eloquent `with()` with filtering based on relation

I have this tables.
And this model relations, this relations works fine.
class Item extends Model
{
public function translations()
{
return $this->hasMany(ItemTranslations::class);
}
}
class ItemTranslation extends Model
{
public function language()
{
return $this->belongsTo(Language::class);
}
}
I need to return a list of items with the translations, but only the translations related to a specific language.
I can't have this query working, im getting all translations of each item, not only the one filtered with this query. The language related to the translation is not needed on the result.
$query = Item::query();
$query->with('translations')->when('language',function($query) use ($ISOlanguage) {
return $query->where('languages.ISO_code', '=', $ISOlanguage);
});
return $query->paginate();
Any idea who i can have this working? Thanks!
So what you want to do is constraining eager loading
Item::with(["translations" => function ($query) use ($ISOlanguage) {
$query->where('language.ISO_code', $ISOlanguage);
}])->get();
https://laravel.com/docs/5.8/eloquent-relationships#constraining-eager-loads
I finally have it working
Item::with(['translations' => function($query) use ($ISOlanguage) {
$query->whereHas('language', function($query) use ($ISOlanguage) {
$query->where('ISO_code', '=', $ISOlanguage);
});
}])->get();
Thanks #julian-s for your help!

Laravel - Return only multiple relationships with records from controller or model

I'm returing a model with all the related models.
My problem is that perhaps some models don't have records so they are returned in my $property variable as empty and I have to evaluate their existence in blade (not done yet)
Is there any way in controller or parent model to return only the child relationships which have records? Or any blade directive to evaluate these cases?
All relationships are 1:M.
Regards!
Controller Code
class PropertyController extends Controller
{
public function details($id)
{
$property = Property::with('attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services')
->where('prop_id', $id)
->first();
// dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}
}
Sir u have the same result with this code?? (just trying to help, im new in this world)
return view('pages.processes.offerdemand.matchs.propertymodal', compact(array('property')));
class PropertyController extends Controller
{
public function details($id)
{
$relations = ['attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services']
$property = Property::with($relations)->whereHas($relations)
->where('prop_id', $id)
->first();
// dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}
}
SOLUTION
I changed code as follows and recieved the expected result:
public function details($id)
{
$property = Property::with(['attributes' => function ($builder) {
$builder->where('pa_value', '!=', '');
}])
->with('addons', 'distributions', 'images', 'attributes_2', 'services')
->where('prop_id', $id)
->withCount('attributes', 'addons', 'distributions', 'images', 'attributes_2', 'services')
->first();
dd($property);
return view('pages.processes.offerdemand.matchs.propertymodal', compact('property'));
}

Laravel query scope combination

I'm using Laravel 4.2 Query Scopes but encountered a problem.
My Model:
class SomeModel extends Eloquent {
public function scopeS1($query) {
return $query->where('field1', '=', 'S1');
}
public function scopeS2($query) {
return $query->where('field2', '=', 'S2');
}
}
Now when I do SomeModel::s1()->s2()->get(); it returns all results and doesn't filter by S1 AND S2. Note also that I have no problem when I do
SomeModel::where('field1', '=', 'S1')->where('field2', '=', 'S2')->get()
So why is query scoping and doing anything here??
Since your real scopes contain OR conditions you should use a nested where to make sure they get interpreted correctly. Laravel will wrap parentheses around.
public function scopeS1($query) {
return $query->where(function($q){
$q->where('field1', '=', 'S1')
->orWhere('foo', '=', 'bar');
});
}
// and the same for scopeS2...

Resources