[Quick 4.2.4] New Entity and Instance Not Loaded Blockers

I’m struggling with being able to access relationships on a new instance of a Quick entity. I keep getting the dreaded “This instance is not loaded so it cannot access the […] relationship” error.

My entity is set up with a belongsToMany relationship like this:

// Gallery Entity
component
    extends="models.BaseQuickEntity"  // <-- custom base class which extends quick.models.BaseEntity
    accessors="true"
    table="gallery"
{

    property name="id";
    property name="name";

    variables.media = [];

    /**
     * HasMany: Gallery belongsToMany Media
     */
    function media() {
        return belongsToMany( "Media", "gallery_media", "galleryId", "mediaId" );
    }

Since my form can work with existing and new entities, I would like to param the defaults in the rc scope, but when I try to execute the following, I get the error. I feel like Quick should return an empty array here, instead of requiring the instance to be loaded. (note: code has been simplified for sake of example)

function add( event, rc, prc ) {
        
    prc.gallery = getInstance( "Gallery" ).newEntity();
    rc.append( prc.gallery.getMemento( "media" )  );
    
}

Is there a way to bypass this blocker for new entities and have it return [] for the relationship?

I created a workaround which seems to fix the issue for me. I’m not exactly sure why the blocker is in place to prevent accessing relationships for unloaded entities, so hopefully this doesn’t introduce any negative side-effects.

The workaround I used is as follows:
All of my Quick entities extend a local base class, BaseQuickEntity, which allows me to provide some universal functionality (like filtering, sorting, etc) to all of my entities. I added the following method to the base class which eliminates the guard.

// eliminate the guard
private void function guardAgainstNotLoaded( required string errorMessage ) {
}

Once added, querying the relationship on an unloaded entity returns an empty array as expected.

The check is in place because in the first versions of Quick people would try to access a relationship before loading an entity, usually because they forgot to fetch the entity, and then spent lots of time debugging to find out they forgot their first or findOrFail call.

Another way to temporarily disable this is to wrap the call in the ignoreLoadedGuard function.

function add( event, rc, prc ) {
        
    prc.gallery = getInstance( "Gallery" ).newEntity();
    prc.gallery.ignoreLoadedGuard( function() {
         rc.append( prc.gallery.getMemento( "media" )  );
    } );
    
}

I’m curious the use case where you would want a memento of something not persisted in your database, but it really doesn’t matter. Both of these approaches work fine.

1 Like

Thanks @elpete! It’s good to know about the ignoreLoadedGuard() function. I missed that when scanning the Quick source code.

The reason why I feel it’s important to be able to pull mementos from non-persisted elements, is because:

  1. Since creating/updating entities based on form data often involves duplicate logic, I can create much more DRY code by handling creates/updates with the same code. I can share the pattern I use if you are curious.

  2. I run validateModel() calls against populated entities before they are persisted. I do not like having business logic in handlers, and have found that simply validating the rc scope doesn’t cover most use-cases I have come across. Often times, entities require recursive validation of sub entities (relationships) in order to validate (e.g. a Gallery must have at least one valid Media). Therefore it is very important to be able to construct an object in its entirety before persistence.

  3. I have been taught that persistence is an implementation detail, so I like my entities to behave independently of whether they have been persisted or not.