Clicking through to related records using Laravel SmartCollection

Feature(s) impacted

When creating a SmartCollection, how do we get the user to click through to a specific relation like the generated collections?

E.g. clicking on a record below, or even the artist ‘name’ I want to take the user to that artist record. The relations all exist but not sure that the smartcollection is aware of it - how do I declare the relationship?

The docs specify how to do this in SQL and MongoDB but not using Laravel implementation:

I’d assume I could use a similar decleration to the js approach, but this just causes the view to not render the ID column.

    public function fields(): Collection
    {
        return collect(
            [
                new SmartField(
                    [
                        'field' => 'id',
                        'type'  => 'Number',
                        'reference' => 'artists.id'
                    ]
                ),

It does however seem to read this when editing the layout:

For reference the artists table referred to on the SmartField does exist here by that name, with an id field (also shown, id not rendering):

Also, I’ve tried ‘artist.id’ as the reference as this is what the underlying class is in the forest schema and at /data/artist/index, but then returns this console error

Observed behavior

Doesn’t render column if using a relationship on a smart field

Expected behavior

Renders the column with a click through to the related record

Failure Logs

Context

  • Project name: …
  • Team name: …
  • Environment name: …
  • Agent type & version: … Laravel 1.2.1
  • Recent changes made on your end if any: …

Hi @EFGP,

I am able to reproduce the problem.
I will investigate and keep you informed with the progress

1 Like

Hi @EFGP,

I just released version 1.2.3 which fixes this problem.

In your case, you need to add a BelongsTo smart relation to your smart collection.

There is an example of implementation in the following documentation.

Brilliant thanks so much! I’ll give this a whirl tomorrow hopefully and report back.

Hi @EFGP ,

Would you mind giving us your findings ? Is it fixed on your side ? If so, please mark this thread as solved ! :pray:

Cheers,

Nicolas,

Hey,

Not sure where I’m going wrong here, the Artist model is a real database model, the artistList is the virtual forestadmin collection.

I’ve noticed that the documentation for the collection has the model extending SmartCollection, but then in the smartRelationship documentation it extends Model? Since we can’t have both in PHP is it meant to be model?

The below returns

Base table or view not found: 1146 Table '2023_01_12.artist_lists' doesn't exist

But can only assume this is because I’ve switched the extends decleration from the SmartCollection class to Model class.

If I use SmartCollection now, to how it was working before, I get

"Return value of ForestAdmin\\LaravelForestAdmin\\Http\\Controllers\\ResourcesController::getModel() must be an instance of Illuminate\\Database\\Eloquent\\Model, instance of App\\Models\\SmartCollections\\ArtistList returned"


namespace App\Models\SmartCollections;

use App\Models\Artist;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use ForestAdmin\LaravelForestAdmin\Services\SmartFeatures\SmartField;
use ForestAdmin\LaravelForestAdmin\Services\Concerns\ForestCollection;
use ForestAdmin\LaravelForestAdmin\Services\SmartFeatures\SmartCollection;
use ForestAdmin\LaravelForestAdmin\Services\SmartFeatures\SmartRelationship;

class ArtistList extends Model
{
    use HasFactory, ForestCollection;

    protected string $name = 'artistList';

    protected bool $is_searchable = true;

    protected bool $is_read_only = true;

    /**
     * @return SmartRelationship
     */
    public function artist(): SmartRelationship
    {
        return $this->smartRelationship(
            [
                'type' => 'Number',
                'reference' => 'artists.id'
            ]
        )
            ->get(
                function () {
                    return Artist::where('id', $this->id)
                        ->first();
                }
            );
    }

Hi @EFGP :wave:

Since this is not a Laravel model, I confirm that it must extend from SmartCollection.

Given the error, I assume it is a route conflict.
Have you implemented the routes for this Smartcollection in a custom controller and in web/routes.php?
More information : here

Coming back to this, the route is called fine, and returns data in the first instance

"Return value of ForestAdmin\\LaravelForestAdmin\\Http\\Controllers\\ResourcesController::getModel() 
must be an instance of Illuminate\\Database\\Eloquent\\Model, instance of 
App\\Models\\SmartCollections\\ArtistList returned" 

The view returns fine with this in my ArtistList SmartCollection

 public function fields(): Collection
    {
        return collect(
            [
                new SmartField(
                    [
                        'field' => 'id',
                        'type'  => 'Number'
                    ]
                ),
                new SmartField(
                    [
                        'field' => 'name',
                        'type'  => 'String',
                    ]
                ),
            ]
        );
    }

| | GET|HEAD | forest/artistList | generated::l2T1V6efxiwV18dj | App\Http\Controllers\Forest\ArtistListsController@index | web

namespace App\Http\Controllers\Forest;

use App\Models\Artist;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Laravel\Horizon\Http\Controllers\Controller;
use ForestAdmin\LaravelForestAdmin\Facades\JsonApi;

class ArtistListsController extends Controller
{
    /**
     * @return JsonResponse
     */
    public function index(): JsonResponse
    {
        $artistLists = Artist::select(DB::raw('artists.id, artists.name'))
            ->orderBy('artists.created_at');

        if (request()->has('search')) {
            $artistLists->whereRaw("LOWER (artists.name) LIKE LOWER(?)", ['%' . request()->input('search') . '%']);
        }

        $pageParams = request()->query('page') ?? [];

        return response()->json(
            JsonApi::render(
                $artistLists->paginate(
                    $pageParams['size'] ?? null,
                    '*',
                    'page',
                    $pageParams['number'] ?? null
                ),
                'artistList',
            )
        );
    }
}

But then I’m unsure how I’m meant to reference the SmartRelationship - will it just be picked up like normal BelongsTo in Laravel? Or do I need to attach return it along with the fields() method here? And assume it lives in the SmartCollection ArtistList, not the Eloquent Model Artist?

Note, ArtistList is a Virtual smartcollection, but Artist is an Eloquent Model.

For all intents it looks like the documentation suggests.

From what I can see ResourcesController performs getModel() using the collection name - but my collection is a SmartCollection (ArtistList) so will never return Eloquent\Model on ResourcesController:65 → callAction()

Hi @EFGP,

If I understand correctly, you have the following error when you call the forest/artistList route?

"Return value of ForestAdmin\\LaravelForestAdmin\\Http\\Controllers\\ResourcesController::getModel() 
must be an instance of Illuminate\\Database\\Eloquent\\Model, instance of 
App\\Models\\SmartCollections\\ArtistList returned"

If so, you can try to clear the routes cache with the following command: php artisan route:clear

For the smartRelationships, they will be automatically loaded with the JsonApi::render method.

Hi,

Yes as below, we’ve ran optimize previously and ran route:clear just to be sure

Don’t know if this helps but:

This is the URL when I click through to this custom collection model:

/data/directoryArtistList/index/record/directoryArtistList/1307/details?filter=&page=1&search=&searchExtended=false&segmentId=&sort=

which throws the controller error.

However this works (since the artist record is what the smartrelation is, clicking on a custom directoryArtistList ‘artist’ should take me to the artist record).

/data/directoryArtistList/index/record/artist/1307/details?filter=&page=1&search=&searchExtended=false&segmentId=&sort=

Also, clicking the record in Forest throws the getModel() error, the forest/artistList route itself is fine.

Have you implemented the “show” (get single record) route?

If not, you must implement the “show” route if you want to click on a specific record in a smartCollection.

1 Like

OK thanks for your patience - i’ll give that a try, I think I was confusing the SmartRelationship with the functionality when you click into a record.

1 Like