Resources
A resource is the Qore definition for one Eloquent model.
Resources extend QoreResource<TModel> and describe how that model appears in Qore: CRUD pages, fields, permissions, actions, relation tables, global search, logbook entries and URLs.
Use a resource when a model needs the standard application surface. A working resource normally has five app pieces:
- the resource class;
- registration in
AppServiceProvider; - seeded permissions;
- a model policy;
- a menu item.
Create A Resource Class
Create the resource in app/Resources. This example assumes an App\Models\Product model with name and price columns.
<?php
namespace App\Resources;
use App\Models\Product;
use Illuminate\Database\Eloquent\Model;
use Qore\Next\System\Field\Field;
use Qore\Next\System\Fields\CreatedAtField;
use Qore\Next\System\Fields\NumberField;
use Qore\Next\System\Fields\TextField;
use Qore\Next\System\Fields\UpdatedAtField;
use Qore\Next\System\Resource\QoreResource;
/**
* @extends QoreResource<Product>
*/
class ProductResource extends QoreResource
{
/**
* @return class-string<Product>
*/
public function modelClass(): string
{
return Product::class;
}
public function labelSingular(): string
{
return __('common.products.singular');
}
public function labelPlural(): string
{
return __('common.products.plural');
}
/**
* @param Product $model
*/
public function modelTitle(Model $model): string
{
return $model->name;
}
public function icon(): string
{
return 'package';
}
/**
* @return Field[]
*/
public function fields(): array
{
return [
TextField::make('name', __('common.name'))
->setRules(fn () => ['required', 'string', 'max:255']),
NumberField::make('price', __('common.price'))
->setRules(fn () => ['required', 'numeric', 'min:0'])
->setPrecision(2),
CreatedAtField::make(),
UpdatedAtField::make(),
];
}
}
ProductResource becomes the resource name products. Qore uses that name for default URLs and permission names, such as view any products.
At minimum a resource defines:
modelClass()labelSingular()labelPlural()icon()fields()
Register The Resource
Register resources in AppServiceProvider::register() so Qore can expose their routes, globals, relation options and menu metadata.
use App\Resources\ProductResource;
use function Qore\Next\System\Helpers\qore;
public function register(): void
{
qore()
->registerResource(new UserResource)
->registerResource(new ProductResource);
}
Registering the resource does not grant access by itself. Permissions and policies still decide what the user may do.
Retrieve A Registered Resource
Use getResource() when the resource may be absent and getResourceOrFail() when it must be registered:
$resource = qore()->getResource('products');
$resource = qore()->getResource(Product::class);
$resource = qore()->getResource($product);
$resource = qore()->getResourceOrFail($product);
Both methods accept a resource name, a registered model class or an Eloquent model instance. Model instances are resolved through their morph class. getResource() returns null when no resource is registered; getResourceOrFail() throws a RuntimeException.
Seed Permissions
Add CRUDA permissions for the resource in database/seeders/PermissionSeeder.php.
use Illuminate\Database\Seeder;
use Qore\Next\System\Models\Permission;
use function Qore\Next\System\Helpers\admin_role;
use function Qore\Next\System\Helpers\qore;
class PermissionSeeder extends Seeder
{
public function run(): void
{
qore()->permissions()->cruda('products');
admin_role()->givePermissions(Permission::query()->select('id')->get());
}
}
cruda('products') creates the default resource permissions:
view any productsview productscreate productsupdate productsdelete products
Use the same resource name everywhere unless the resource overrides its permission methods. See Permissions for custom permission names.
Create The Policy
Create a policy for the model in app/Policies/ProductPolicy.php.
namespace App\Policies;
use App\Models\Product;
use App\Models\User;
class ProductPolicy
{
public function viewAny(User $user): bool
{
return $user->hasPermissionToViewAny('products');
}
public function view(User $user, Product $model): bool
{
return $user->hasPermissionToView('products', $model);
}
public function create(User $user): bool
{
return $user->hasPermissionToCreate('products');
}
public function update(User $user, Product $model): bool
{
return $user->hasPermissionToUpdate('products', $model);
}
public function delete(User $user, Product $model): bool
{
return $user->hasPermissionToDelete('products', $model);
}
}
Laravel auto-discovers policies that follow the normal App\Models\Product and App\Policies\ProductPolicy naming convention. Register the policy manually only when the class lives outside that convention:
Gate::policy(Product::class, ProductPolicy::class);
Add It To The Menu
Add the resource to the application menu, usually in app/Menu/AppMenu.php.
use function Qore\Next\System\Helpers\qore;
$this->addResourceMenuItem(resource: qore()->getResourceOrFail('products'));
addResourceMenuItem() uses the resource label, icon, index URL and getViewAnyPermissions() automatically. If the user cannot viewAny, the frontend hides the menu item from the globals payload.
Resource Checklist
When a resource does not show up, check these in order:
- The resource is registered in
AppServiceProvider::register(). - The resource name used in permissions matches
ProductResource -> products. - The permission seeder has run and the current role has the permissions.
- The policy returns
truefor the action you are testing. - The menu includes the resource or a custom menu item that points to it.
Common Overrides
Most resources start with the required methods and override only what changes the user experience:
modelTitle(Model $model): stringfor readable titles.modelDescription(Model $model): ?stringfor optional context in relation options and global search.indexQuery(): Builderfor default scoping and eager loading.optionsQuery(): Builderfor relation select options, attach-existing choices and global search queries. It defaults toindexQuery().getSelectOption(Model $model): SelectOptionfor relation option labels and descriptions. Keep the option value as the model key.indexTabs(): TableTab[]for index table tabs.indexLayout(): Node,createLayout(): Node,detailLayout(Model $model): NodeandeditLayout(Model $model): Nodefor custom page layouts.formFieldLayout(array $fields): ?Nodefor a shared create/edit form field layout while keeping Qore's default create and edit pages.actions(): ResourceAction[]for bulk and detail actions.alerts(): ResourceAlert[]for detail-page warnings or notices.tags(Model $model): TagNode[]for tags next to the detail-page actions.getGlobalSearchFields(): Field[]andglobalSearchNode(Model $model): Nodefor search behaviour.
getOptions(SelectMetadata $metadata) invokes getOptionsCallback() and returns normalized SelectOption values. The callback may return any iterable of SelectOption, SelectOptionable or Eloquent model values. Models that do not implement SelectOptionable are resolved through their registered resource.
See the focused pages for the details: