statamic-rad-pack/runway

MorphToMany relationships not displaying in front end.

Closed this issue · 4 comments

Description

In runway v5, I was working with MorphToMany relationships and the front and back ends displayed correctly.
In v6, I am not unable to loop over the relationship on the front end despite the backend displaying correctly.

Error in log:

LOG.debug: Cannot loop over non-loopable variable: {{ titles }}

Displays fine on backend
Screenshot 2024-01-24 at 2 57 41 PM

Display on frontend

<div>
    Field name: {{ name }}
</div>
<div>
    Computed city_and_state: {{ city_and_state }}
</div>
<div>
    Title Relationship
    {{ titles }}
    {{ sort_title }}
    {{ /titles }}
{{#     LOG.debug: Cannot loop over non-loopable variable: {{ titles }} { #}}
{{#     "line": 9, #}}
{{#     "file": "\/var\/www\/html\/resources\/views\/runway\/city\/show.antlers.html" #}}
</div>
Screenshot 2024-01-24 at 3 00 54 PM

Steps to reproduce

runway.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Resources
    |--------------------------------------------------------------------------
    |
    | Configure the resources (models) you'd like to be available in Runway.
    |
    */

    'resources' => [
         \App\Models\Title::class => [
             'cp_icon' => 'book-open',
             'name' => 'Titles',
             'route' => '/journals/{{ abbreviation }}',
             'title_field' => 'sort_title',
             'template' => 'runway.journal.show',
             'relationships' => ['cities'],
         ],
        \App\Models\City::class => [
            'cp_icon' => 'flag',
            'name' => 'Cities',
            'route' => '/cities/{{ slug }}',
            'title_field' => 'city_state_and_country',
            'template' => 'runway.city.show',
            'read_only' => true,
            'relationships' => ['titles'],
        ],

City Model

class City extends Model
{
    use HasRunwayResource;
    use RunwayRoutes;

    public $guarded = [];

    protected $appends = ['coordinates', 'city_state_and_country'];

    protected $with = ['titles'];

    public function titles(): MorphToMany
    {
        return $this->morphedByMany(Title::class, 'cityable');
    }

    public function cityStateAndCountry(): Attribute
    {
        return Attribute::make(
            get: fn () => ( is_numeric($this->state_code) ) ? $this->name .', ' . $this->country_code : $this->name .', ' . $this->state_code.', ' . $this->country_code,
        )->shouldCache();
    }

    public function cityAndState(): Attribute
    {
        return Attribute::make(
            get: fn () => ( is_numeric($this->state_code) ) ? $this->name .', ' . $this->country_code : $this->name .', ' . $this->state_code,
        )->shouldCache();
    }

    public function coordinates(): Attribute
    {
        return Attribute::make(
            get: fn () => !empty($this->latitude)&&!empty($this->longitude) ? '['. $this->latitude .',' . $this->longitude.']' : '',
        )->shouldCache();
    }

    public function slug(): Attribute
    {
        return Attribute::make(
            get: fn () =>  Str::of($this->cityStateAndCountry)->slug('-')
        )->shouldCache();
    }
}

City blueprint

tabs:
  main:
    display: Main
    sections:
      -
        fields:
          -
            handle: city_state_and_country
            field:
              type: text
              display: City
              visibility: computed
          -
            handle: slug
            field:
              type: text
              display: Slug
              visibility: computed
              listable: false
              instructions_position: above
              replicator_preview: true
              input_type: text
              antlers: false
              hide_display: false
          -
            handle: country_id
            field:
              type: integer
              display: CountryId
              validate: required
              listable: false
          -
            handle: state_id
            field:
              type: integer
              display: StateId
              validate: required
              listable: false
          -
            handle: name
            field:
              type: text
              display: Name
              validate: required
          -
            handle: country_code
            field:
              type: text
              display: CountryCode
              validate: required
          -
            handle: state_code
            field:
              type: text
              display: StateCode
              validate: required
          -
            handle: latitude
            field:
              type: text
              display: Latitude
              validate: required
          -
            handle: longitude
            field:
              type: text
              display: Longitude
              validate: required
          -
            handle: city_and_state
            field:
              input_type: text
              antlers: false
              type: text
              display: city_and_state
              icon: text
              listable: hidden
              instructions_position: above
              visibility: computed
              replicator_preview: true
              hide_display: false
      -
        display: Relationships
        fields:
          -
            handle: titles
            field:
              mode: default
              resource: title
              create: false
              title_format: '{{ title }}'
              reorderable: false
              type: has_many
              display: Titles
              icon: has_many
              listable: hidden
              instructions_position: above
              visibility: visible
              replicator_preview: true
              hide_display: false
  sidebar:
    display: Sidebar
    sections:
      - {  }
title: City

Title Model

class Title extends Model
{
    use HasRunwayResource;
    use RunwayRoutes;

    public $guarded = ['backend_extra_attributes'];

    public $with = ['cities'];

    public function cities(): MorphToMany
    {
        return $this
            ->morphToMany(City::class, 'cityable');
    }

    /**
     * Get the title attribute.
     * Will return the display_title if set. Otherwise, returns sort_title
     */
    public function title(): Attribute
    {
        return Attribute::make(
            get: fn () =>  !empty($this->display_title)  ? $this->display_title : $this->sort_title,
        )->shouldCache();
    }
}

Title blueprint

tabs:
  main:
    display: Main
    sections:
      -
        fields:
          -
            handle: sort_title
            field:
              type: text
              display: 'Sort Title'
              validate: required
          -
            handle: display_title
            field:
              type: text
              display: DisplayTitle
              listable: hidden
              instructions: 'OPTIONAL! Only use if you need the title to display in something other than the sortable title. Example- If Sort Title is Mississippi Rag, The then enter The Mississippi Rag here.'
          -
            handle: abbreviation
            field:
              type: text
              display: Abbreviation
          -
            handle: title
            field:
              input_type: text
              antlers: false
              type: text
              display: Title
              icon: text
              listable: false
              instructions_position: above
              visibility: computed
              replicator_preview: true
              hide_display: false
              validate:
                - image
      -
        display: Relationships
        fields:
          -
            handle: cities
            field:
              mode: default
              resource: city
              create: false
              reorderable: false
              type: has_many
              display: Cities
              icon: has_many
              listable: hidden
              instructions_position: above
              visibility: visible
              replicator_preview: true
              hide_display: false
sidebar:
  fields:
    -
      handle: created_at
      field:
        type: date
        display: CreatedAt
        listable: hidden
    -
      handle: updated_at
      field:
        type: date
        display: UpdatedAt
        listable: hidden
title: Title

Environment

Environment
Application Name: App_name
Laravel Version: 10.42.0
PHP Version: 8.2.14
Composer Version: 2.6.6
Environment: local
Debug Mode: ENABLED
URL: app_name.test
Maintenance Mode: OFF

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: statamic
Database: mysql
Logs: stack / single
Mail: smtp
Queue: sync
Session: file

Statamic
Addons: 1
Antlers: runtime
Stache Watcher: Enabled
Static Caching: Disabled
Version: 4.45.0 PRO

Statamic Addons
statamic-rad-pack/runway: 6.0.4

I have done a bit more digging. If I comment out the relationships for the resource in runway.php, I get the following error:

Object of class Illuminate\Database\Eloquent\Relations\MorphToMany could not be converted to string

With the relationships defined the dump is returning Illuminate\Database\Eloquent\Relations\MorphToMany
But I am unable to loop through the resource
Screenshot 2024-01-30 at 3 27 50 PM

I have traced this back to
/statamic-rad-pack/runway/src/Resource.php line 137
public function eloquentRelationships(): Collection

I am not familiar enough with the project to troubleshoot any further, but I will keep digging.

city/show.antlers.html

Resource name field {{ name }} <br>
Resource computed field {{ city_state_and_country }} <br>
Relationship:<br>
{{ titles | dump }}<br>
Nothing here:
{{ titles }}
    {{ slug }}
{{ /titles }}

runway.php

    'resources' => [
         \App\Models\Title::class => [
             'cp_icon' => 'book-open',
             'name' => 'Journals',
             'route' => '/journals/{{ abbreviation }}',
             'title_field' => 'sort_title',
             'template' => 'runway.journal.show',
             'relationships' => ['cities'],
         ],
        \App\Models\City::class => [
            'cp_icon' => 'flag',
            'name' => 'Cities',
            'route' => '/cities/{{ id }}',
            'title_field' => 'city_state_and_country',
            'read_only' => true,
            'template' => 'runway.city.show',
            'layout' => 'debug',
//            'relationships' => ['titles'],
        ],
        ],

Submitted a PR that resolves the issue when relationships is not defined for the resource in runway.php
If the relationship is defined, I am still getting the error

[13:42:49] LOG.debug: Cannot loop over non-loopable variable: {{ titles }} {
    "line": 10,
    "file": "\/var\/www\/html\/resources\/views\/runway\/city\/show.antlers.html"
}

The docs say to devine eager loaded relationships with the key 'relationships'
https://runway.duncanmcclean.com/upgrade-guides/v5-x-to-v6-0#content-low-changes-to-overriding-eager-loaded-relationships
If i change the key from 'relationships' back to 'with', the resource works as expected.


    'resources' => [
         \App\Models\Title::class => [
             'cp_icon' => 'book-open',
             'name' => 'Titles',
             'route' => '/journals/{{ abbreviation }}',
             'title_field' => 'sort_title',
             'template' => 'runway.journal.show',
             'with' => ['cities'],
         ],
        \App\Models\City::class => [
            'cp_icon' => 'flag',
            'name' => 'Cities',
            'route' => '/cities/{{ slug }}',
            'title_field' => 'city_state_and_country',
            'template' => 'runway.city.show',
            'read_only' => true,
            'with' => ['titles'],
        ],

There are still refrences to 'with' in files RunwayTag.php, RunwayTagTest.php and BaseFieldtype.php

Hey 👋

Sorry for not getting back to you regarding this issue. I looked at it briefly last week but kept running into issues getting your example running (not related to your issue) so I moved onto something else and didn't end up looping back to it until now.

Thanks for your PR - I've merged it and it's part of v6.0.5.

Also, on the back of your comment regarding the relationships setting. It seems like I caused some issues as part of my refactoring in v6. I've changed the name of the setting back to with like it was in v5 (#418).

Thanks for the note. I understand that the morph relations are lacking support, but I am using them as read only for now. I just noticed a similar error with MorphTo and will be submitting a PR for that shortly.

Great package by the way!