Laravel 5.8 query scope - laravel

I am learning laravel and I encountered this problem where when I use query scope my code returns zero data. The database has got data.
It is kinda confusing because I think I have done everything right as per the tutorial
Scope:
public static function scopeLatest($query)
{
return $query->orderBy('id', 'asc')->get();
}
Controller:
public function index()
{
$posts = Post::Latest();
return view('posts.index', compact('posts'));
}

AFAIK, you won't be able to use scopeLatest as Laravel already has a latest() method on its query builder.
As for the scope you tried to make, here are a few pointers:
a scope shouldn't be defined as static method
you shouldn't actually call get() inside your scope
You don't need to return from the scope.
So even though this scope won't actually work (because of the name), as an example, this is what is would look like based on your question:
public function scopeLatest($query)
{
$query->orderBy('id', 'desc'); //desc should put the latest first
}
Your controller method (in either case) should be:
public function index()
{
$posts = Post::latest()->get();
return view('posts.index', compact('posts'));
}

Related

Laravel get value from 2 column with relationship one to one

I have 2 tables and have relationship one to one. Second table use a FK from first one, and I want to display a list with all values .
public function index(Request $request)
{
$listOfPersons = new Person();
$listOfRegisters = new Register();
$listOfRegisters->listOfPersons()->associate();
return $listOfRegisters;
}
In Register Model
public function people(){
return $this->hasOne(Person::class);
}
In Person Model
public function register(){
return $this->hasOne(Register::class);
}
If you just want a list with all pairs of values, it should be enough with this code:
public function index(Request $request)
{
$registers = Register::all();
$list = [];
foreach($registers as $register){
array_push($list,['register'=> $register, 'person'=>$register->people]);
}
return $list;
}
But remember you can just have the list of registers and access the person via the relationship. Moreover, you should change the hasOne relationship to belongsTo in register.
I hope that helps.
I think you have to use leftjoin. (not foreach and php loop)
Because:
The alternative of handling this inside your PHP code with a foreach
loop is unattractive for several reasons. First, you would probably
need to bring in all information from both tables, which is wasteful
from both a memory and network usage point of view. Then, even after
you have brought in the data, you would be relying on PHP to perform
the join. PHP wasn't really designed for in house database operations,
and cannot use something like an index to speed up the process.
So you can write your query like:
User::leftJoin('register', 'register.user_id', '=', 'id');
However, I prefer to add a scope in my model for this situation
<?php
class User extends Authenticatable
{
public function scopeRegister($builder)
{
$query = $query->leftJoin('register', 'register.user_id', '=', 'id');
return $query;
}
and in my controller
public function index(Request $request)
{
$records = User::register()->get();
}

Laravel error using with() method in Eloquent

I'm trying to call using with() method in Laravel Eloquent ORM, but getting the following error.
Argument 1 passed to
App\Http\Controllers\DashboardController::App\Http\Controllers\{closure}()
must be an instance of Illuminate\Database\Eloquent\Builder,
instance of Illuminate\Database\Eloquent\Relations\HasMany given
I'm using the latest version of Laravel 6. Any ideas what might have caused this?
Controller
class DashboardController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
public function formIndex(Request $request)
{
$id = auth()->user()->id;
$item = Groupe::find($id)->with(
[
'etudiants' => function (Builder $query) {
$query->select('id');
}
]
)->first();
return $item;
}
}
Model
class Groupe extends Authenticatable implements JWTSubject
{
public function etudiants()
{
return $this->hasMany('App\Etudiant');
}
}
The error comes from the type hint you put on the $query variable, as the error message said the object that gets passed in there is a relationship not a raw Query Builder. Just remove the type hint. Also ::find() executes a query, so you're be executing 2 queries, use the query below instead
Groupe::where('id', $id)->with(['etudiants' => function ($query) {
$query->select('id');
}])->first();
Additionally, you don't need to use the callback syntax to only eager load certain columns, the callback syntax is for putting contraints on which records get returned. Try this instead.
Groupe::where('id', $id)->with('etudiants:id,name,email')->first();
But what do you want return? groupe->students[]?? you can use
$item = Groupe::where('id',$id)->with('etudiants')->first();

How can I make pagination of a findorfail method in laravel

I have this function in my controller which is the output of many animals and I would like to paginate it. How am I to do that
controller
public function show($id)
{
$farms = User::with(['animals'])->findOrFail($id);
return view('slaughter.show',compact('farms'));
}
Is there any other way of doing that because I have tried to add the paginate method at the end and I am getting an error
paginate() is a method on the Query Builder instance while findOrFail() returns an Eloquent model, and in this case a single instance, as it is for a single user by the given $id.
You can maybe query the animals through their own model, and paginate it, like this:
public function show($id)
{
$farms = Animal::where('user_id', $id)->paginate(10);
return view('slaughter.show',compact('farms'));
}
I would do this to get a paginated collection of animals for the User. Dependency Injection in the show method as well assuming you also require some user data on the slaughter page.
public function show(User $user)
{
$farms = Animals::where('user_id', $user->id)->paginate();
return view('slaughter.show',compact('farms', 'user'));
}
Your route will need updating, something like this...
Route::get('animals/{user}', 'AnimalsController#show')

Laravel 5: Eloquent relationship method with conditions

Normal relationship methods don't usually have a condition, and tend to look like this:
class StripeCustomer extends Model
{
public function user()
{
return $this->belongsTo(User::class, 'stripe_customer_id');
}
}
In my model I have a condition in the relationship method like so:
class StripeCustomer extends Model
{
public function user()
{
if ($this->type === 'normal') {
return $this->hasOne(User::class, 'stripe_customer_id');
} else {
return $this->hasOne(User::class, 'stripe_customer_charity_id');
}
}
}
Does Laravel support conditional relationships in Eloquent like above. A lot of the usual methods still work like so:
StripeCustomer::get()->first()->user;
StripeCustomer::get()->first()->user()->get();
But would the following work predictably:
Foo::with('user')->get();
The issue here is that I am unsure in how the "with" operator works in Eloquent internally.
A reason I believe it also doesn't work is that the user() method needs to be executed for every model. However, when I added a dump(...) at the start of the method, I found it was only run once, indicating that with() does not work.
No, it won't work with with(). What do you think will happen when you try to execute the following code:
Foo::with('user')->get();
The answer is Laravel will create new instance of Foo and try to call user() to get the relationship object. This new instance doesn't have any type ((new Foo)->type will be null), therefore your method user() will always return $this->hasOne(Bar::class, 'b_id') and this relationship object will be used to construct a query.
As you can see this is clearly not what you wanted since only type B users will be eager loaded for all Foo rows. What you need to do in this case is create two relationships (one for each type) and accessors (get/set) for user:
class Foo extends Model
{
public function userA()
{
return $this->hasOne(Bar::class, 'a_id');
}
public function userB()
{
return $this->hasOne(Bar::class, 'b_id');
}
public function getUserAttribute()
{
if ($this->type === 'a') {
return $this->userA;
} else {
return $this->userB;
}
}
public function setUserAttribute($user)
{
if ($this->type === 'a') {
$this->userA()->associate($user);
} else {
$this->userB()->associate($user);
}
}
}
Then you can use with() for both relations to utilize eager loading:
$fooRows = Foo::with('userA', 'userB')->get();
...
foreach ($fooRows as $row) {
$row->user;
}
edit:
Since you've edited code in your question the example code in my answer no longer represents your case, but I hope you get the overall idea.
Yep, with() works. It runs a subquery on any relation your user() method returns. Since your relation already has a constraint, it applies said constraint to the subquery as you'd expect.

Laravel query scope - how is $query attributes passed?

public function scopePublished($query) {
$query->where('published_at', '<=', Carbon::now());
}
How is the $query parameter passed here? Does the function pass the parameter $query to itself?
Here is the function call:
public function index() {
$articles = Article::latest('published_at')->published()->get();
return view('articles.index', compact('articles'));
}
The Model class itself passes on everything all missing method calls, except increment() and decrement(), to the underlying Query object. This is done via magic __call() and __callStatic() methods.
The Query object, \Illuminate\Database\Eloquent\Builder, then does this:
if (method_exists($this->model, $scope = 'scope'.ucfirst($method))) {
return $this->callScope($scope, $parameters);
}
I.e it checks if there is a method with the method name prefixed with scope() available on the model, and if so - it simply calls it and passing it self (i.e the Query).

Resources