Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default ordering resource #156

Closed
franzdumfart opened this issue Aug 24, 2018 · 26 comments
Closed

Default ordering resource #156

franzdumfart opened this issue Aug 24, 2018 · 26 comments

Comments

@franzdumfart
Copy link

franzdumfart commented Aug 24, 2018

Found nothing in the docs, so here is my question:

How can I set the default ordering of a Resources items on list view? At the moment it looks like it is orderBy('id', 'desc'). Setting the first visible and sortable field would be a great indicator for the planned behavior. Or is possible the set it via a property or method?

@crnkovic
Copy link

I’m on mobile now but can you try overriding indexQuery method on the resource and put it there?

@danrichards
Copy link

danrichards commented Aug 24, 2018

@crnkovic is correct @franzdumfart

This can be accomplished by overloading the indexQuery method.

Unfortunately the solution is slightly more fugly than probably desired, because the order by id beats the request to the query. But this works fine.

    const DEFAULT_INDEX_ORDER = 'last_name';

    public static function indexQuery(NovaRequest $request, $query)
    {
        $query->when(empty($request->get('orderBy')), function(Builder $q) {
            $q->getQuery()->orders = [];

            return $q->orderBy(static::DEFAULT_INDEX_ORDER);
        });
    }

I should note, that the UI state and url state does not know sorting has been changed. So if you are sorting by a column that is visible by default, the UI will not reflect that.

See suggestion from @jasonlav, this is a bit more desirable.

@franzdumfart
Copy link
Author

Thanks so much, this helps! Will extend all my Nova resources from a Base class and add a helper method there, so I can use it on every Resource with just setting one property. Hopefully this will be implemented someday in Nova itself. Need this very often.

@loren138
Copy link

loren138 commented Aug 25, 2018

Slightly, updated code to make it one line per Resource.

In App/Nova/Resource.php:

    public static $defaultSort = null; // Update to your default column
    
    /**
     * 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)
    {
        if (static::$defaultSort && empty($request->get('orderBy'))) {
            $query->getQuery()->orders = [];
            return $query->orderBy(static::$defaultSort);
        }
        return $query;
    }

In any resource where you want to change the sort:

public static $defaultSort = 'name';

@franzdumfart
Copy link
Author

Thanks @loren138, works perfect! We also should add another static variable like public static $defaultSortDirection = null; to define the direction. I think we can close this one.

@crnkovic
Copy link

I added protected static $sort = ['id' => 'desc']; and then returned $query->orderBy(key(static::$sort), reset(static::$sort)); :)

I hate that PHP doesn't have tuples, god damn.

Nice solution, though. :)

@loren138
Copy link

@franzdumfart Probably shouldn't close it until it is a feature request or a pull request.

@franzdumfart
Copy link
Author

I've ended with this code in App\Nova\Resource

/**
 * Default ordering for index query.
 *
 * @var array
 */
public static $indexDefaultOrder = [
    'id' => 'desc'
];

/**
 * 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)
{
    if (empty($request->get('orderBy'))) {
        $query->getQuery()->orders = [];
        return $query->orderBy(key(static::$indexDefaultOrder), reset(static::$indexDefaultOrder));
    }
    return $query;
}

Thanks for all the suggestions! 🙌

@telkins
Copy link

telkins commented Aug 26, 2018

How 'bout sorting on a relation field...?

For example, activities belong to an event and an event has a date. If you view activities, you may want to sort on their event dates.

@taylorotwell
Copy link
Member

This repository is for tracking bugs. Feature requests may be emailed to Nova customer support.

@loren138
Copy link

loren138 commented Sep 7, 2018

@taylorotwell Is the customer support email listed somewhere on the Nova website? The only email I've been able to find so far is your email from the composer.json file.

@phoenixg
Copy link

@taylorotwell Is it possible to open a nova-feature-requests repository, so we can all submit to that?

@jasonlav
Copy link

jasonlav commented Sep 25, 2018

The applyOrderings method from Laravel\Nova\PerformsQueries can be overridden on a per-model basis to set default ordering.

@carloshc
Copy link

carloshc commented Dec 4, 2018

It's seems that this approach does not works on relationship models.

So I put a orderBy() on my Model.

    public function comments()
    {
	    return $this->belongsToMany('App\Posts')
					->withPivot('order')
                                          ->orderBy('order');
    }

@shane-smith
Copy link

shane-smith commented Dec 4, 2018

I'm trying to resolve this myself at the moment, could you provide a more detailed example please @jasonlav? Attempting to set the default sort order for a HasMany-related resource.

e.g. Town resource has many Roads, which appear as a relationship when viewing a Town. Attempting to simply sort the Roads panel by name ascending, without success. Perhaps my brain is just broken at the moment :)

@jasonlav
Copy link

jasonlav commented Dec 7, 2018

The PerformsQueries trait -- implemented by Nova's Resource class -- has an applyOrderings method that can be overwritten on the Nova Resource for custom sorting.

    /**
     * Apply any applicable orderings to the query.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  array  $orderings
     * @return \Illuminate\Database\Eloquent\Builder
     */
    protected static function applyOrderings($query, array $orderings)
    {
        if (empty($orderings)) {

            // To maintain user-initiated column sorting, I would recommend placing your custom sorting here.

            return empty($query->orders)
                        ? $query->latest($query->getModel()->getQualifiedKeyName())
                        : $query;
        }

        foreach ($orderings as $column => $direction) {
            $query->orderBy($column, $direction);
        }

        return $query;
    }

@aozen
Copy link

aozen commented Jan 11, 2019

public static $indexDefaultOrder = [
    'id' => 'desc'
];


public static function indexQuery(NovaRequest $request, $query)
{
    if (empty($request->get('orderBy'))) {
        $query->getQuery()->orders = [];
        return $query->orderBy(key(static::$indexDefaultOrder), reset(static::$indexDefaultOrder));
    }
    return $query;
}

This code is working. But i have several questions.
I am using this query without order

      $companyID= Company::where('user_id', auth()->user()->id)->first()->id;
         return $query->where('company_id', $companyID)->first();

This is showing users own data. Other users informations are not displaying.
When i use with order query its showing all data. All user can display all datas. How can i add my own query with order?

I made this. But when sort some column. All datas showing up again...

public static $indexDefaultOrder = [
    'id' => 'desc'
];


public static function indexQuery(NovaRequest $request, $query)
{
    $companyID= Company::where('user_id', auth()->user()->id)->first()->id;
    if (empty($request->get('orderBy'))) {
        $query->where('company_id', $companyID)->getQuery()->orders = [];
        return $query->where('company_id', $companyID)->orderBy(key(static::$indexDefaultOrder), reset(static::$indexDefaultOrder));
    }
    return $query;
}

How can i solve this. How can i use order and display limit (each user) together?

@rasmuscnielsen
Copy link

The PerformsQueries trait -- implemented by Nova's Resource class -- has an applyOrderings method that can be overwritten on the Nova Resource for custom sorting.

    /**
     * Apply any applicable orderings to the query.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  array  $orderings
     * @return \Illuminate\Database\Eloquent\Builder
     */
    protected static function applyOrderings($query, array $orderings)
    {
        if (empty($orderings)) {

            // To maintain user-initiated column sorting, I would recommend placing your custom sorting here.

            return empty($query->orders)
                        ? $query->latest($query->getModel()->getQualifiedKeyName())
                        : $query;
        }

        foreach ($orderings as $column => $direction) {
            $query->orderBy($column, $direction);
        }

        return $query;
    }

Using this approach I ended up with a simple overwrite in my base Resource file

    protected static function applyOrderings($query, array $orderings)
    {
        if (empty($orderings) && property_exists(static::class, 'orderBy')) {
            $orderings = static::$orderBy;
        }
        
        return parent::applyOrderings($query, $orderings);
    }

Now in any Resource I can simple add

    public static $orderBy = ['name' => 'asc'];

Works like a charm

@SHAH0001
Copy link

I used sorting by overriding the indexQuery method. But when I try to apply filters they do not work. What could be the reason?

@sonalmahajan01
Copy link

I have my resource like :
` ID::make()->sortable(),

        Text::make('Content', 'content', function() {
            return '<a href="' . $this->getContentLink() . '" target="_blank">'.$this->content->title->title.'</a>';
        })->asHtml()->hideWhenUpdating()->sortable(),
        
        Text::make('Seller', function(){
            return $this->content->organization->company_name;
        })->hideWhenUpdating()->sortable(),
        
        Text::make('Buyer', function() {
            if(empty($this->parent_offer_id) || $this->created_by_buyer == 1) {
                return $this->user->organization->company_name;
            }else {
                $parentOffer = AppOffer::find($this->parent_offer_id);
                return $parentOffer->user->organization->company_name;
            }
        })->hideWhenUpdating()->sortable(),`

The sorting doesn't work on the seller and buyer. What is the way to implement it ?

@bakerkretzmar
Copy link

For anyone else still coming back to this issue, Nova v3.19.1 introduced changes that (at least for me) broke @rasmuscnielsen's awesome workaround. Nova now explicitly orders all index queries by a resource model's primary key by default, so to get the snippet above working again, I had to remove the orderBy clauses manually:

    protected static function applyOrderings($query, array $orderings)
    {
        if (empty($orderings) && property_exists(static::class, 'orderBy')) {
+           $query->reorder();

            $orderings = static::$orderBy;
        }

        return parent::applyOrderings($query, $orderings);
    }

@rdarcy1
Copy link

rdarcy1 commented Feb 4, 2021

If you sort a field and cycle through until you're not sorting any more (click the field name three times), you can end up with a null entry in the $orderings array, e.g. ['quantity' => null].

To make sure you're still applying your custom default sort, filter the orderings array:

    protected static function applyOrderings($query, array $orderings)
    {
-       if (empty($orderings) && property_exists(static::class, 'orderBy')) {
+       if (empty(array_filter($orderings)) && property_exists(static::class, 'orderBy')) {
           $query->reorder();

            $orderings = static::$orderBy;
        }

        return parent::applyOrderings($query, $orderings);
    }

@sfinktah
Copy link

sfinktah commented Nov 8, 2021

@rdarcy1 That's fixed by now (3.29 anyway).

It now looks like this (I have changed latest to oldest because that's the order I wanted).

    protected static function applyOrderings($query, array $orderings)
    {
        $orderings = array_filter($orderings);

        if (empty($orderings)) {
            return empty($query->getQuery()->orders) && ! static::usesScout()
                        ? $query->oldest($query->getModel()->getQualifiedKeyName())
                        : $query;
        }

        foreach ($orderings as $column => $direction) {
            $query->orderBy($column, $direction);
        }

        return $query;
    }

@benedict-w
Copy link

Is this still the best way with Nova 4?

@JshGrn
Copy link

JshGrn commented Jun 14, 2022

^ +1, Nova 4 still the best way? Seems strange it wasn't implemented if so

@sfinktah
Copy link

Still the same solution, nothing implemented in Nova 4.

@laravel laravel locked and limited conversation to collaborators Jan 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests