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

After updated Nova package doesn't work #24

Open
LorenzoAlu opened this issue Nov 12, 2023 · 8 comments
Open

After updated Nova package doesn't work #24

LorenzoAlu opened this issue Nov 12, 2023 · 8 comments

Comments

@LorenzoAlu
Copy link

Hello

First of all, I congratulate you on a job well done.

Second I wanted to open this issue because after updating to the latest version of nova 4.29.5 the package unfortunately stops working.

I attach image with the error.

Screenshot 2023-11-12 alle 14 11 20
@milewski
Copy link
Member

Hi I'm not able to reproduce this issue, can you show me some code of how are you using this package?

@LorenzoAlu
Copy link
Author

LorenzoAlu commented Nov 18, 2023

sure. :)

This is models:

Course

<?php declare(strict_types=1);

namespace App\Nova\Resources\v1;

use App\Enums\CourseType;
use App\Enums\RoleType;
use App\Models\Database\v1\Course;
use App\Models\Database\v1\User;
use App\Nova\Filters\v1\CourseDateTimeFilter;
use App\Nova\Filters\v1\TrainerFilter;
use App\Nova\Metrics\v1\AcceptedParticipants;
use App\Nova\Metrics\v1\AcceptedPerRejectedParticipants;
use App\Nova\Metrics\v1\NewParticipants;
use App\Nova\Metrics\v1\NewParticipantsPerDay;
use App\Nova\Metrics\v1\PresentParticipants;
use App\Nova\Metrics\v1\PresentsPerAbsents;
use App\Nova\Metrics\v1\RejectedParticipants;
use Carbon\Carbon;
use Closure;
use Database\Constants\MigrationConstants;
use DigitalCreative\CustomRelationshipField\CustomRelationshipField;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Schema\Builder;
use Illuminate\Http\Request;
use Laravel\Nova\Card;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\BelongsToMany;
use Laravel\Nova\Fields\DateTime;
use Laravel\Nova\Fields\FormData;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Textarea;
use Laravel\Nova\Http\Requests\NovaRequest;
use ShuvroRoy\NovaTabs\Tab;
use ShuvroRoy\NovaTabs\Tabs;
use ShuvroRoy\NovaTabs\Traits\HasTabs;

/**
 * Class CourseResource.
 *
 * @mixin Course
 */
final class CourseResource extends Resource
{
    use HasTabs;

    /**
     * @var class-string<Course>
     */
    public static string $model = Course::class;

    /**
     * {@inheritdoc}
     */
    public static $search = [
        'title',
    ];

    /**
     * {@inheritdoc}
     */
    public static $title = 'title';

    /**
     * {@inheritdoc}
     */
    public function cards(NovaRequest $request): array
    {
        /** @var string $cardsWidth */
        $cardsWidth = auth()->user()?->hasAnyOfRoles([
            RoleType::ROLE_TRAINER()->value,
            RoleType::ROLE_SUPERVISOR()->value,
        ]) ?
            Card::ONE_HALF_WIDTH :
            Card::ONE_THIRD_WIDTH;

        return [
            (new NewParticipants())->onlyOnDetail()
                                   ->width($cardsWidth)
                                   ->canSee(
                                       fn (Request $request): bool => $this->isAdmin($request)
                                   ),

            (new AcceptedParticipants())->onlyOnDetail()
                                        ->canSee(
                                            fn (Request $request): bool => $this->isAdmin($request)
                                        ),

            (new RejectedParticipants())->onlyOnDetail()
                                        ->canSee(
                                            fn (Request $request): bool => $this->isAdmin($request)
                                        ),

            (new PresentParticipants())->onlyOnDetail()
                                       ->width($cardsWidth)
                                       ->canSee(
                                           fn (Request $request): bool => $this->isTrainer($request) ||
                                               $this->isSupervisor($request)
                                       ),

            (new PresentsPerAbsents())->onlyOnDetail()
                                      ->canSee(
                                          fn (Request $httpRequest): bool => $this->isAdmin($httpRequest) &&
                                              self::getCourse($request)->is_in_past
                                      ),

            (new AcceptedPerRejectedParticipants())->onlyOnDetail()
                                                   ->canSee(
                                                       fn (
                                                           Request $httpRequest
                                                       ): bool => $this->isAdmin($httpRequest) &&
                                                           self::getCourse($request)->is_in_past
                                                   ),

            (new NewParticipantsPerDay())->onlyOnDetail()
                                         ->canSee(
                                             fn (Request $request): bool => $this->isAdmin($request)
                                         ),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function fields(NovaRequest $request): array
    {
        return [
            Tabs::make('Corso', [
                Tab::make(__('Dettagli Corso'), [
                    Text::make(__('Titolo'), 'title')
                        ->rules([
                            'required',
                            'max:' . Builder::$defaultStringLength,
                        ])
                        ->sortable(),

                    DateTime::make(__('Data Inizio'), 'start_date')
                            ->rules([
                                'required',
                                'after_or_equal:today',
                            ])
                            ->step(Carbon::MINUTES_PER_HOUR)
                            ->filterable(),

                    DateTime::make(__('Data Fine'), 'end_date')
                            ->dependsOn(
                                ['start_date'],
                                static function (DateTime $field, NovaRequest $request, FormData $formData): void {
                                    if (!$formData->get('start_date')) {
                                        $field->readonly();
                                    }

                                    $field->min($formData->get('start_date') ?? 'tomorrow');
                                }
                            )
                            ->rules([
                                'required',
                                'after:valid_from',
                            ])
                            ->step(Carbon::MINUTES_PER_HOUR)
                            ->filterable(),

                    Select::make(__('Tipo'), 'type')
                          ->rules('required')
                          ->options(static fn (): array => CourseType::toArray())
                          ->displayUsingLabels()
                          ->filterable(),

                    Text::make(__('Città'), 'city')
                        ->hide()
                        ->dependsOn(
                            ['type'],
                            $this->showIfCourseIsInSite()
                        )
                        ->filterable(),

                    Text::make(__('Location'), 'location')
                        ->hide()
                        ->dependsOn(
                            ['type'],
                            $this->showIfCourseIsInSite()
                        )
                        ->suggestions(
                            Course::pluck('location')->filter()
                                  ->values()
                                  ->toArray()
                        )
                        ->filterable(),

                    BelongsTo::make(__('Formatore'), 'user', UserResource::class)
                             ->relatableQueryUsing(
                                 fn (
                                     NovaRequest $request,
                                     EloquentBuilder $query
                                 ): EloquentBuilder => $this->isAdmin($request) ?
                                     $query->withTrainerRole() :
                                     $query->whereId($request->user()?->getKey())
                             )
                             ->rules('required')
                             ->withoutTrashed()
                             ->default(fn ($request) => $this->isTrainer($request) ? $request->user()->getKey() : null),

                    Textarea::make(__('Descrizione'), 'description')
                            ->rules([
                                'required',
                                'max:' . MigrationConstants::SUPER_LONG_STRING_MAX_LENGTH,
                            ])
                            ->sortable(),
                ]),
            ]),

            Tabs::make(__('Sezione Partecipanti'), [
                HasMany::make(__('Partecipanti in attesa'), 'participants', PendingCourseParticipantResource::class)
                       ->canSee(fn (Request $request): bool => $this->isAdmin($request)),

                HasMany::make(__('Partecipanti Accettati'), 'participants', AcceptedCourseParticipantResource::class),

                HasMany::make(__('Partecipanti Rifiutati'), 'participants', RejectedCourseParticipantResource::class)
                       ->canSee(fn (Request $request): bool => $this->isAdmin($request)),
            ]),

            Tabs::make(__('Sezione Follow Up'), [
                HasMany::make(__('Follow Up Singolo'), 'followUpSingles', FollowUpSingleResource::class),

                BelongsToMany::make(__('Follow Up Multiplo'), 'followUpMultiples', FollowUpMultipleResource::class),
            ]),

            HasMany::make(__('Clienti Invitati'), 'shops', CourseInvitationResource::class),

            Tabs::make(__('Sezione Inviti'), [
                CustomRelationshipField::make(__('Punti Vendita Selezionati'), 'selectedShops', SelectedShopResource::class)
                                       ->canSee(fn (
                                           Request $request
                                       ) => $this->isAdmin($request) && !$this->is_in_past),

                CustomRelationshipField::make(__('Seleziona Punti Vendita'), 'unselectedShops', UnselectedShopResource::class)
                                       ->canSee(fn (
                                           Request $request
                                       ) => $this->isAdmin($request) && !$this->is_in_past),
            ]),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function filters(NovaRequest $request): array
    {
        return [
            new CourseDateTimeFilter(),
            (new TrainerFilter())
                ->canSee(fn () => $this->isAdmin($request)),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public static function indexQuery(NovaRequest $request, $query): EloquentBuilder
    {
        /** @var User $user */
        $user = $request->user();

        if ($user->hasRole(RoleType::ROLE_TRAINER()->value)) {
            return $query->whereUserId($user->id);
        }

        return $query;
    }

    /**
     * {@inheritdoc}
     */
    public static function label(): array|string|null
    {
        return __('Corsi');
    }

    /**
     * {@inheritdoc}
     */
    public static function singularLabel(): array|string|null
    {
        return __('Corso');
    }

    /**
     * @return Closure
     */
    private function showIfCourseIsInSite(): Closure
    {
        return static function (Text $field, NovaRequest $request, FormData $formData): void {
            if ($formData->get('type') === CourseType::ONSITE()->value) {
                $field->show();
            }
        };
    }
}

and this is Unselected shop Resource

<?php declare(strict_types=1);

namespace App\Nova\Resources\v1;

use App\Models\Database\v1\Course;
use App\Nova\Actions\v1\SelectedShop;
use App\Nova\Filters\v1\CustomerFilter;
use App\Policies\Traits\ShopListsPolicy;
use DigitalCreative\CustomRelationshipField\CustomRelationshipFieldTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Laravel\Nova\Http\Requests\NovaRequest;

/**
 * Class UnselectedShopResource.
 */
final class UnselectedShopResource extends WithShopAttributes
{
    use CustomRelationshipFieldTrait;
    use ShopListsPolicy;

    /**
     * @var string
     */
    private const CONFIRM_BUTTON_TEXT = 'Seleziona Punti Vendita';

    /**
     * @var string
     */
    private const CONFIRM_TEXT = 'Selezionare Punto Vendita?';

    /**
     * @return CustomerFilter
     */
    public function getCustomerFilter(): CustomerFilter
    {
        return new CustomerFilter(true);
    }

    /**
     * {@inheritdoc}
     */
    public static function label(): array|string|null
    {
        return __('Invita Punti Vendita');
    }

    /**
     * {@inheritdoc}
     */
    public static function singularLabel(): array|string|null
    {
        return __('Invita Punto Vendita');
    }

    /**
     * @param NovaRequest $request
     *
     * @return array
     */
    public function unselectedShopsActions(NovaRequest $request): array
    {
        return [
            SelectedShop::make()
                        ->showInline()
                        ->confirmText(self::CONFIRM_TEXT)
                        ->confirmButtonText(self::CONFIRM_BUTTON_TEXT),
        ];
    }

    /**
     * @param NovaRequest $request
     * @param EloquentBuilder $query
     * @param Model $model
     *
     * @return EloquentBuilder
     */
    public static function unselectedShopsQuery(
        NovaRequest $request,
        EloquentBuilder $query,
        Model $model
    ): EloquentBuilder {
        if (!$model instanceof Course) {
            self::cleanQuerySorting($request, $query);

            return $query->orderBy(self::DEFAULT_SORTING_COLUMN);
        }

        self::cleanQuerySorting($request, $query);

        /** @var EloquentBuilder $query */
        $query = $query->active()
                       ->withExternalManagersMail()
                       ->whereDoesntHave(
                           'courses',
                           fn (EloquentBuilder $query): EloquentBuilder => $query->where('course_id', $model->id)
                       );

        return $query->orderBy(self::DEFAULT_SORTING_COLUMN);
    }
}

if you want more information I'm here ;)

Package works perfect with Nova 4.27.12,but after this release I have that error in more Nova Resources classes.

@milewski
Copy link
Member

milewski commented Nov 20, 2023

Can you isolate the issue? for example remove as much as possible from your CourseResource class to find when it breaks? I suspect the issue is with the Tabs package,

Also, your CustomRelationshipFieldTrait trait is in the wrong file, it should be on CourseResource instead

@LorenzoAlu
Copy link
Author

Sure ;)

/**
 * Class CourseResource.
 *
 * @mixin Course
 */
final class CourseResource extends Resource
{
    use HasTabs;

    ... 
  
    /**
     * {@inheritdoc}
     */
    public function fields(NovaRequest $request): array
    {
        return [
            Tabs::make('Corso', [
                Tab::make(__('Dettagli Corso'), [
                  
                    ..... 

                ]),
            ]),

            Tabs::make(__('Sezione Partecipanti'), [
                    .....
            ]),

            Tabs::make(__('Sezione Follow Up'), [
                    ......
            ]),

            .....
            
            Tabs::make(__('Sezione Inviti'), [
                CustomRelationshipField::make(__('Punti Vendita Selezionati'), 'selectedShops', SelectedShopResource::class)
                                       ->canSee(fn (
                                           Request $request
                                       ) => $this->isAdmin($request) && !$this->is_in_past),

                CustomRelationshipField::make(__('Seleziona Punti Vendita'), 'unselectedShops', UnselectedShopResource::class)
                                       ->canSee(fn (
                                           Request $request
                                       ) => $this->isAdmin($request) && !$this->is_in_past),
            ]),
        ];
    }

    ...

}
/**
 * Class UnselectedShopResource.
 */
final class UnselectedShopResource extends WithShopAttributes
{
    use CustomRelationshipFieldTrait;
    use ShopListsPolicy;

    ......

    /**
     * @param NovaRequest $request
     *
     * @return array
     */
    public function unselectedShopsActions(NovaRequest $request): array
    {
        return [
            SelectedShop::make()
                        ->showInline()
                        ->confirmText(self::CONFIRM_TEXT)
                        ->confirmButtonText(self::CONFIRM_BUTTON_TEXT),
        ];
    }

    /**
     * @param NovaRequest $request
     * @param EloquentBuilder $query
     * @param Model $model
     *
     * @return EloquentBuilder
     */
    public static function unselectedShopsQuery(
        NovaRequest $request,
        EloquentBuilder $query,
        Model $model
    ): EloquentBuilder {
        if (!$model instanceof Course) {
            self::cleanQuerySorting($request, $query);

            return $query->orderBy(self::DEFAULT_SORTING_COLUMN);
        }

        self::cleanQuerySorting($request, $query);

        /** @var EloquentBuilder $query */
        $query = $query->active()
                       ->withExternalManagersMail()
                       ->whereDoesntHave(
                           'courses',
                           fn (EloquentBuilder $query): EloquentBuilder => $query->where('course_id', $model->id)
                       );

        return $query->orderBy(self::DEFAULT_SORTING_COLUMN);
    }
}

Error occurs in CourseResource index only afteI update package.
If you need more code ask me :D

@milewski
Copy link
Member

Lol but I have the same setup as you (just don't have the tabs traits/package) and it is working on my end...

Could that be WithShopAttributes has some other traits in there interfering with it?

The only thing could cause this issue is if you have other traits that are also overriding the same methods as the CustomRelationshipFieldTrait

@LorenzoAlu
Copy link
Author

Unfortunaly that class doesn't have trait :(

<?php declare(strict_types=1);

namespace App\Nova\Resources\v1;

use App\Models\Database\v1\Shop;
use App\Nova\Filters\v1\CustomerFilter;
use App\Nova\Filters\v1\ExternalManagerFilter;
use App\Nova\Filters\v1\MultiSelectFilter;
use App\Rules\v1\IsValidateEmail;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Schema\Builder;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\Boolean;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\Number;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
use Outl1ne\MultiselectField\Multiselect;

/**
 * Class WithShopAttributes.
 *
 * @mixin Shop
 */
abstract class WithShopAttributes extends Resource
{
    /**
     * @var int
     */
    public const MAX_MAIL_NUMBER = 4;

    /**
     * @var string
     */
    protected const DEFAULT_SORTING_COLUMN = 'shop_name';

    /**
     * @var class-string<Shop>
     */
    public static string $model = Shop::class;

    /**
     * {@inheritdoc}
     */
    public static $search = [
        'shop_code',
        'shop_name',
        'street',
        'city',
    ];

    /**
     * {@inheritdoc}
     */
    public static $title = 'shop_name';

    /**
     * {@inheritdoc}
     */
    public function fields(NovaRequest $request): array
    {
        return [
            Number::make(__('Codice Porta'), 'shop_code')
                  ->rules('required')
                  ->sortable(),

            Text::make(__('Ragione Sociale'), 'shop_name')
                ->rules([
                    'required',
                    'max:' . Builder::$defaultStringLength,
                ])
                ->sortable(),

            Text::make(__('Indirizzo'), 'street')
                ->rules([
                    'max:' . Builder::$defaultStringLength,
                ])
                ->sortable(),

            Text::make(__('Città'), 'city')
                ->rules([
                    'max:' . Builder::$defaultStringLength,
                ])
                ->sortable(),

            Text::make(__('Provincia'), 'province')
                ->rules([
                    'max:' . Builder::$defaultStringLength,
                ])
                ->sortable(),

            Text::make(__('Regione'), 'region')
                ->rules([
                    'max:' . Builder::$defaultStringLength,
                ])
                ->sortable(),

            Text::make(__('CAP'), 'cap')
                ->hideFromIndex()
                ->rules([
                    'max:' . Builder::$defaultStringLength,
                ])
                ->sortable(),

            Multiselect::make('Email Referenti Esterni', 'external_managers_mail')
                       ->taggable()
                       ->max(self::MAX_MAIL_NUMBER)
                       ->rules([
                           new IsValidateEmail(),
                       ])
                       ->saveAsJSON()
                       ->sortable(),

            Boolean::make(__('Attivo'), 'is_active')
                   ->filterable(),

            BelongsTo::make(__('Cliente'), 'customer', CustomerResource::class)
                     ->withoutTrashed()
                     ->searchable(),

            BelongsTo::make(__('Referente Interno'), 'internalManager', InternalManagerResource::class)
                     ->nullable()
                     ->withoutTrashed(),

            HasMany::make(__('Partecipanti'), 'participants', ParticipantResource::class),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function filters(NovaRequest $request): array
    {
        return [
            $this->getCustomerFilter(),
            new ExternalManagerFilter(),
            new MultiSelectFilter(__('Provincia'), 'province'),
            new MultiSelectFilter(__('Citta'), 'city'),
            new MultiSelectFilter(__('Regione'), 'region'),
        ];
    }

    /**
     * @return CustomerFilter
     */
    abstract public function getCustomerFilter(): CustomerFilter;

    /**
     * @param NovaRequest $request
     * @param EloquentBuilder $query
     *
     * @return void
     */
    protected static function cleanQuerySorting(NovaRequest $request, EloquentBuilder $query): void
    {
        if (!$request->get('orderBy')) {
            $query->getQuery()->orders = [];
        }
    }
}

@lollocoverzen
Copy link

hi again :)

I'll try to add that method in UnselectedShopResource class

/**
 * Class UnselectedShopResource.
 */
final class UnselectedShopResource extends WithShopAttributes
{
   
    /**
     * @param NovaRequest $request
     *
     * @return array
     */
    public function unselectedShopsActions(NovaRequest $request): array
    {
        return [
            SelectedShop::make()
                        ->showInline()
                        ->confirmText(self::CONFIRM_TEXT)
                        ->confirmButtonText(self::CONFIRM_BUTTON_TEXT),
        ];
    }

    public function unselectedShopsFields(NovaRequest $request)
    {
        return $this->fields($request);
    }
}

Visualization works. But when i try to trigger an action response in always 404.

Screenshot 2024-01-20 alle 17 16 04 Screenshot 2024-01-20 alle 17 16 09

I don't know why with 4.27.12 this package works perfect. I try to remove Tabs package but application not works :(

@lollocoverzen
Copy link

Further notice :)

After investigating the error it seems the package update itself is causing the error. I thought I had the latest version installed. instead I am at version 1.0.0.
The package stops working when switching from this version to the latest one, v1.1.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants