owen-it / laravel-auditing

Record the change log from models in Laravel
https://laravel-auditing.com
MIT License
3.06k stars 390 forks source link

Laravel Nova: Class "users" not found OR Unknown column 'users_id' in 'field list' #834

Closed eComEvo closed 1 year ago

eComEvo commented 1 year ago
Q A
Bug? yes
New Feature? no
Framework Laravel
Framework version 10.12.0
Package version 13.5
PHP version 8.1.17
Laravel Nova 4.24.4

Actual Behaviour

Instead of creating a new audit record when Laravel Nova creates a record, it gives this error:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users_id' in 'field list' (Connection: mysql, SQL: insert into `audits` (`old_values`, `new_values`, `event`, `auditable_id`, `auditable_type`, `users_id`, `users_type`, `tags`, `ip_address`, `user_agent`, `url`, `updated_at`, `created_at`) values ([], {\"service\":\"Dummy\",\"name\":\"Test\",\"description\":null,\"status\":\"inactive\",\"type\":\"default\",\"meta\":\"[]\",\"id\":588}, created, 588, projects, 101, users, ?, 127.0.0.1, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36, https://mysite.test/nova-api/projects?editMode=create&editing=true, 2023-05-26 16:29:32, 2023-05-26 16:29:32))"

The users_id column should actually be user_id.

If I change the morph_prefix in config/audit.php to user (singular) it resolves the creation error. However, doing so then causes a retrieval error when the model is loaded with the audits relation and we get this error instead:

Class "users" not found

To see the new audit relation I then have to re-edit the config/audit.php file to change the morph_prefix back to users (plural). However, this will then break all future audit record creations.

Expected Behaviour

Having the morph_prefix match the value returned in the User::getMorphClass() method would allow it to save a new audit record and retrieve that audit relation without having to change the morph_prefix each time.

Steps to Reproduce

Install Laravel Nova v4 with the auditing package.

Apply the Auditable class to a model and then add this to the related auditable Laravel Nova resource:

MorphMany::make('Audits')->onlyOnDetail(),

Add a Nova Audit resource:

namespace App\Nova;

use Illuminate\Http\Request;
use Laravel\Nova\Fields\DateTime;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\KeyValue;
use Laravel\Nova\Fields\MorphTo;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;

class Audit extends Resource
{
    /**
     * The model the resource corresponds to.
     *
     * @var class-string<\OwenIt\Auditing\Models\Audit>
     */
    public static $model = \OwenIt\Auditing\Models\Audit::class;

    /**
     * The single value that should be used to represent the resource when being displayed.
     *
     * @var string
     */
    public static $title = 'user_type';

    /**
     * Indicates if the resource should be displayed in the sidebar.
     *
     * @var bool
     */
    public static $displayInNavigation = false;

    /**
     * Indicates if the resource should be globally searchable.
     *
     * @var bool
     */
    public static $globallySearchable = false;

    /**
     * Get the fields displayed by the resource.
     *
     * @param  NovaRequest  $request
     * @return array
     */
    public function fields(NovaRequest $request)
    {
        return [
            ID::make()->sortable(),

            Text::make('User Type')
                ->onlyOnDetail()
                ->nullable(),

            MorphTo::make('User')
                ->readonly()
                ->nullable(),

            Text::make('Event')
                ->rules('required'),

            MorphTo::make('Auditable')
                ->searchable()
                ->rules('required'),

            KeyValue::make('Old Values')
                ->onlyOnDetail()
                ->nullable()
                ->rules('json'),

            KeyValue::make('New Values')
                ->onlyOnDetail()
                ->nullable()
                ->rules('json'),

            Text::make('URL')
                ->nullable()
                ->onlyOnDetail(),

            Text::make('IP Address')
                ->nullable()
                ->onlyOnDetail(),

            Text::make('User Agent')
                ->nullable()
                ->onlyOnDetail(),

            DateTime::make('Created', 'created_at')
                ->readonly(),
        ];
    }

    /**
     * Get the cards available for the request.
     *
     * @param  NovaRequest  $request
     * @return array
     */
    public function cards(NovaRequest $request)
    {
        return [];
    }

    /**
     * Get the filters available for the resource.
     *
     * @param  NovaRequest  $request
     * @return array
     */
    public function filters(NovaRequest $request)
    {
        return [];
    }

    /**
     * Get the lenses available for the resource.
     *
     * @param  NovaRequest  $request
     * @return array
     */
    public function lenses(NovaRequest $request)
    {
        return [];
    }

    /**
     * Get the actions available for the resource.
     *
     * @param  NovaRequest  $request
     * @return array
     */
    public function actions(NovaRequest $request)
    {
        return [];
    }

    public static function authorizedToCreate(Request $request)
    {
        return false;
    }

    public function authorizedToDelete(Request $request)
    {
        return false;
    }

    public function authorizedToUpdate(Request $request)
    {
        return false;
    }
}

Try creating a new resource in Nova that is auditable while morph_prefix is set to the plural form of users then change back to the singular form.

Change the morph_prefix to singular user and try viewing the resource that now has an audit record, then change it back to the plural form.

parallels999 commented 1 year ago

Change the morph_prefix to singular user and try viewing the resource that now has an audit record, then change it back to the plural form.

Why would you change morph_prefix? It's supposed to be fixed, because it's a database field https://github.com/owen-it/laravel-auditing/blob/7d09546436ff3317edc899362c9bc20af27be39c/config/audit.php#L27-L28 https://github.com/owen-it/laravel-auditing/blob/7d09546436ff3317edc899362c9bc20af27be39c/database/migrations/audits.stub#L21-L25

eComEvo commented 1 year ago

I ordinarily wouldn't change morph_prefix....except that I figured out that the plurality difference was the only thing causing the audit relationship to break.

If I leave it as the default user value then none of the audit relationships load and instead give the error Class "users" not found...but if I change it to users then that fixes the loading issue but causes an issue with saving new audit records as I detailed above.

parallels999 commented 1 year ago

if you are going to change the morph_prefix you have to change the database field name too, this is not a bug

eComEvo commented 1 year ago

If I don't change the morph_prefix then the audit relationships don't load because Class "users" not found

parallels999 commented 1 year ago

You are mixing two different things, Class "users" not found means that the class aliases User::getMorphClass() is not correct, morph_prefix is an alias for the database fields, this shouldn't necessarily be equal with the morph class

Let morph_prefix same as db fields, and check your morph class, enforcing-morph-maps-in-laravel, custom-polymorphic-types

if you don't want to check anything, you could also change all the db data in the $morphPrefix . '_type' field to mach the correct User::getMorphClass()

eComEvo commented 1 year ago

You're right, there was an issue with the morph maps being incorrectly defined. I fixed the definition and that resolved the problem. Sorry for the mistaken report.