Display specific eloquent query in nova resource index view - laravel

I want to display the following eloquent in index view for nova resource
Post::where('frontpage', true)->get()
And perform Post model CRUD operations, How can I do that?

You can simply override indexQuery of your Nova resource, Reference
/**
* Build an "index" query for the given resource.
*
* #param \Laravel\Nova\Http\Requests\NovaRequest $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #return \Illuminate\Database\Eloquent\Builder
*/
public static function indexQuery(NovaRequest $request, $query)
{
return $query->where('frontpage', true);
}

Nova utilizes the concept of lenses to do this.
Create a new lens from the command line:
php artisan nova:lens IsFrontpage
Modify the query() method in app/Nova/Lenses/IsFrontpage.php:
public static function query(LensRequest $request, $query)
{
return $request->withOrdering($request->withFilters(
$query->where('frontpage', '=', true)
));
}
Attach the lens to a resource:
public function lenses(Request $request)
{
return [
new IsFrontpage()
];
}
Access the lens in the Nova admin panel: /nova/resources/posts/lens/is-frontpage
Take a closer look at the Nova documentation to also customize the URL slug (see uriKey()) and the columns (see fields()).

Related

Is possible in Laravel Nova 4 use a field of nested relation in search fields?

I have the following db:
Showcases (n to 1) Workers (1 to 1) Users
I need in the showcase resource section find showcase by user's name. In the Nova's documentation they explains that is possible search by related field like this:
public static $search = [
'id', 'author.name'
];
If I try 'worker.user.name' it doesn't works. Any idea?
You'll have to define it on your Laravel Model, otherwise it wont work.
use Laravel\Nova\Query\Search\SearchableRelation;
/**
* Get the searchable columns for the resource.
*
* #return array
*/
public static function searchableColumns()
{
return ['id', new SearchableRelation('author', 'name')];
}
You can use this package titasgailius/search-relations.
<?php
namespace App\Nova\Resources\OrderManagement;
use App\Nova\Resources\Resource;
use Titasgailius\SearchRelations\SearchesRelations;
class Showcase extends Resource
{
use SearchesRelations;
/**
* The relationship columns that should be searched.
*
* #var array
*/
public static $searchRelations = [
'worker.user' => ['name'],
];
/**
* Get the fields displayed by the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function fields(Request $request)
{
return [];
}
}
*Assuming you have set the belongsTo relationships properly in your models.

Laravel: how model gets injected from route parameter

I've seen the following route:
Route::prefix('/users/{user}')->group(function () {
Route::get('groups/{group}', 'UserGroupController#show');
}
And in UserGroupController:
use App\Group;
public function show(Request $request, User $user, Group $group)
{
dd($group);
}
My question is how does the $group model object gets constructed here from a raw route parameter string?
My guess is laravel's service container does the following magic
(maybe sth like
Injecting the Group model,
then do sth like Group::where('id', $group)->first()
but unsure about this.
You guess right. There is a binding in the core service provider which retrieves model. The bound model is the same if you would call:
$temp = new Group
$model = Group::where($temp->getRouteKeyName(), request()->route('group'))->firstOrFail();
UPD. Actualy just found where it happens:
/**
* Retrieve the model for a bound value.
*
* #param \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation $query
* #param mixed $value
* #param string|null $field
* #return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function resolveRouteBindingQuery($query, $value, $field = null)
{
return $query->where($field ?? $this->getRouteKeyName(), $value);
}

Laravel Nova n+1 problem when running sql inside fields()

For some reason, I need dynamically add columns in the fields method. This is not only dynamic columns but also contains computed fields.
This is very simplified version of what I'm trying to do inside fields():
$additional_fields = [];
Product::visible()->each(function($attr) use (&$additional_fields, $request) {
$additional_fields[] = Text::make($attr->name, function() use ($attr, $request) {
$first_subscription = $this->subscriptions()->whereHas('product', function($q) {
return $q->where('visible', 1);
});
...
}
}
This, of course, causing the N+1 problem as statements for Product and Subscription are executed on every row.
I need to move this piece of code somewhere else and run it only once. I can't figure out how to do this yet.
Your can use indexQuery method in your resource to load relationships
/**
* #param NovaRequest $request
* #param \Illuminate\Database\Eloquent\Builder $query
* #return \Illuminate\Database\Eloquent\Builder
*/
public static function indexQuery(NovaRequest $request, $query)
{
$query = $query->with('relation');
return $query;
}

How to get an array of strings instead of object from with() function?

This is the Laravel ProductController and Products has a many-to-many relationship with Tags.
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::with('tags')->latest()->get();
return response()->json($products);
}
On the json response, if I map the products, product.tag is returning an array of objects.
[{"name": "shirt"}, {"name": "red"}]
Is there a way to get only the name property at the with('tags') at the controller, like:
["shirt", "red"]
Also I have been trying something like this:
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::with(['tags' => function ($query) {
$result = $query->select(['name']);
return $result->get();
}])->latest()->get();
return response()->json($products);
}
Its possible to filter the data inside tags function?
You should probably use a Resource class to return just the items required in your API. This includes being able to process child relationships. See https://laravel.com/docs/6.x/eloquent-resources#generating-resources
Or, you can do it the way you tried with select but more like;
$products = Product::with(['tags' => function ($query) {
return $query->select(['name']);
}])->latest()->get();

Call to a member function on a non-object eloquent attach

I am having an issue with laravel not seeing my tags() method for attaching new tags on a new entry. I keep getting Call to a member function on a non-object when I try to run the method and attach tags to my Tile model. All methods are returning their relations. I followed the same order as the documentation says eloquent.
Controller
$tile = \Tiles\Tile::find($tile_id);
$tile->tags()->attach($tag_array);
Model
<?php namespace Tiles;
use Illuminate\Database\Eloquent\Model;
class Tile extends Model {
/**
* The Tile table
* #var string
*/
protected $table = 'tiles';
/**
* Pivot table for tags
* #var string
*/
protected $pivot = 'tag_tile';
/**
* Get the tags associated with the given tile
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags() {
return $this->belongsToMany('Tiles\Tag', $this->pivot, 'tile_id', 'tag_id')->withTimestamps();
}
}
Try it
Model
public function tags() {
return $this->belongsToMany('Tiles\Tag', $this->pivot, 'tag_id', 'tile_id')->withTimestamps();
}
Thanks for all your help. I figured out the solution. I created a method in my model and pushed each to an array and fed it to the attach method. It works now.

Resources