1. Go to this page and download the library: Download tobento/app-crud library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
tobento / app-crud example snippets
use Tobento\App\AppFactory;
$app = (new AppFactory())->createApp();
// Add directories:
$app->dirs()
->dir(realpath(__DIR__.'/../../'), 'root')
->dir(realpath(__DIR__.'/../app/'), 'app')
->dir($app->dir('app').'config', 'config', group: 'config')
->dir($app->dir('root').'vendor', 'vendor')
->dir($app->dir('app').'views', 'views', group: 'views')
->dir($app->dir('app').'trans', 'trans', group: 'trans')
->dir($app->dir('root').'build/public', 'public');
// Adding boots:
// you might boot error handlers:
$app->boot(\Tobento\App\Boot\ErrorHandling::class);
$app->boot(\Tobento\App\Crud\Boot\Crud::class);
// you might boot:
$app->boot(\Tobento\App\View\Boot\Breadcrumb::class);
$app->boot(\Tobento\App\View\Boot\Messages::class);
// Run the app:
$app->run();
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field\FieldInterface;
use Tobento\App\Crud\Field;
use Tobento\App\Crud\Action\ActionsInterface;
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Action;
use Tobento\App\Crud\Filter\FiltersInterface;
use Tobento\App\Crud\Filter\FilterInterface;
use Tobento\App\Crud\Filter;
use Tobento\Service\Repository\RepositoryInterface;
class ProductsController extends AbstractCrudController
{
/**
* Must be unique, lowercase and only of [a-z-] characters.
*/
public const RESOURCE_NAME = 'products';
/**
* Create a new ProductController.
*
* @param RepositoryInterface $repository
*/
public function __construct(
ProductRepository $repository
) {
$this->repository = $repository;
}
/**
* Returns the configured fields.
*
* @param ActionInterface $action
* @return iterable<FieldInterface>|FieldsInterface
*/
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\PrimaryId::new('id'),
Field\Text::new('sku'),
//...
];
}
/**
* Returns the configured actions.
*
* @return iterable<ActionInterface>|ActionsInterface
*/
protected function configureActions(): iterable|ActionsInterface
{
return [
Action\Index::new(title: 'Products'),
//...
];
}
/**
* Returns the configured filters.
*
* @param ActionInterface $action
* @return iterable<FilterInterface>|FiltersInterface
*/
protected function configureFilters(ActionInterface $action): iterable|FiltersInterface
{
return [
Filter\Columns::new()->open(false),
//...
];
}
}
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Entity\Entity;
use Tobento\App\Crud\Entity\EntityInterface;
use Tobento\Service\Support\Arrayable;
class ProductsController extends AbstractCrudController
{
/**
* Create entity from object.
*
* @param object $object
* @return EntityInterface
*/
public function createEntityFromObject(object $object): EntityInterface
{
// Default mapping:
if ($object instanceof Arrayable) {
return new Entity(
attributes: $object->toArray(),
idAttributeName: $this->entityIdName(),
);
}
if (
method_exists($object, 'toArray')
&& is_array($array = $object->toArray())
) {
return new Entity(
attributes: $array,
idAttributeName: $this->entityIdName(),
);
}
return new Entity(
attributes: (array)$object,
idAttributeName: $this->entityIdName(),
);
}
}
use Tobento\App\Crud\AbstractCrudController;
class ProductsController extends AbstractCrudController
{
/**
* Returns the entity id name.
*
* @return string
*/
protected function entityIdName(): string
{
return 'id';
}
}
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Filter\FiltersInterface;
class ProductsController extends AbstractCrudController
{
/**
* Find entities.
*
* @param FiltersInterface $filters
* @return iterable The found entities.
*/
public function findEntities(FiltersInterface $filters): iterable
{
return $this->repository()->findAll(
where: $filters->getWhereParameters(),
orderBy: $filters->getOrderByParameters(),
limit: $filters->getLimitParameter(),
);
}
}
use Tobento\App\Crud\AbstractCrudController;
class ProductsController extends AbstractCrudController
{
/**
* Store entity.
*
* @param array $attributes
* @return object The created entity
*/
public function storeEntity(array $attributes): object
{
return $this->repository()->create(
attributes: $attributes,
);
}
}
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Entity\EntityInterface;
class ProductsController extends AbstractCrudController
{
/**
* Update entity.
*
* @param int|string $id
* @param array $attributes
* @param EntityInterface $entity
* @return object The updated entity
*/
public function updateEntity(int|string $id, array $attributes, EntityInterface $entity): object
{
return $this->repository()->updateById(
id: $id,
attributes: $attributes,
);
}
}
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Entity\EntityInterface;
class ProductsController extends AbstractCrudController
{
/**
* Delete entity.
*
* @param int|string $id
* @param EntityInterface $entity
* @return void
*/
public function deleteEntity(int|string $id, EntityInterface $entity): void
{
$this->repository()->deleteById(id: $id);
}
}
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field\FieldInterface;
use Tobento\App\Crud\Field;
class ProductsController extends AbstractCrudController
{
/**
* Returns the configured fields.
*
* @param ActionInterface $action
* @return iterable<FieldInterface>|FieldsInterface
*/
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\PrimaryId::new('id'),
Field\Text::new(name: 'sku'),
//...
];
}
}
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Action\ActionsInterface;
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Action;
class ProductsController extends AbstractCrudController
{
/**
* Returns the configured actions.
*
* @return iterable<ActionInterface>|ActionsInterface
*/
protected function configureActions(): iterable|ActionsInterface
{
return [
Action\Index::new(title: 'Products'),
Action\Create::new(title: 'New product'),
Action\Store::new(),
Action\Edit::new(title: 'Edit product'),
Action\Update::new(),
Action\Copy::new(title: 'Copy product'),
Action\Show::new(),
Action\Delete::new(),
Action\BulkDelete::new(),
Action\BulkEdit::new(),
];
}
}
use Tobento\App\Crud\AbstractCrudController;
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Filter\FiltersInterface;
use Tobento\App\Crud\Filter\FilterInterface;
use Tobento\App\Crud\Filter;
class ProductsController extends AbstractCrudController
{
/**
* Returns the configured filters.
*
* @param ActionInterface $action
* @return iterable<FilterInterface>|FiltersInterface
*/
protected function configureFilters(ActionInterface $action): iterable|FiltersInterface
{
return [
Filter\Columns::new()->open(false),
Filter\FieldsSortOrder::new(),
...Filter\Fields::new()
->group('field')
->fields($action->fields())
->toFilters(),
Filter\PaginationItemsPerPage::new()->open(false),
// must be added last!
Filter\Pagination::new(),
];
}
}
use Tobento\App\Boot;
use Tobento\App\Crud\Boot\Crud;
class RoutesBoot extends Boot
{
public const BOOT = [
// you may ensure the crud boot:
Crud::class,
];
public function boot(Crud $crud)
{
$crud->routeController(App\ProductsController::class);
// or:
$crud->routeController(
controller: App\ProductsController::class,
// you may set specific actions only:
only: ['index', 'show'],
// or you may exclude specific actions:
except: ['bulk'],
// you may set middlewares for all routes:
middleware: [
SomeMiddleware::class,
],
// you may localize the routes:
localized: true,
);
}
}
use Tobento\Service\Routing\RouterInterface;
// After adding boots
$app->booting();
$router = $this->app->get(RouterInterface::class);
$name = App\ProductsController::RESOURCE_NAME;
$router->resource($name, App\ProductsController::class);
// needed if you have configured bulk actions:
$router->post($name.'/bulk/{name}', [App\ProductsController::class, 'bulk'])
->name($name.'.bulk');
// Run the app:
$app->run();
use Tobento\App\Crud\Field;
Field\Checkboxes::new(
name: 'colors',
// you may set a label, otherwise name is used:
label: 'Colors',
);
Field\Checkboxes::new('colors')
// specify the options using an array:
->options(['blue' => 'Blue', 'red' => 'Red'])
// or using a closure (parameters are resolved by autowiring):
->options(fn(ColorsRepository $repo): array => $repo->findColors());
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field;
use Tobento\App\Crud\Field\FieldInterface;
Field\Checkboxes::new('colors')
->selected(value: ['blue', 'red'], action: 'create')
// or using a closure (additional parameters are resolved by autowiring):
->selected(
value: function (ActionInterface $action, FieldInterface $field): null|array {
return ['blue', 'red'];
//return null; // if none is selected
},
action: 'edit',
)
use Tobento\App\Crud\Field;
Field\Checkboxes::new('colors')->attributes(['class' => 'name']);
Field\Checkboxes::new('colors')
->validate('
use Tobento\App\Crud\Field;
Field\File::new(
name: 'file',
// you may set a label, otherwise name is used:
label: 'File',
);
use Tobento\App\Crud\Field;
Field\Files::new(name: 'files')
// min only;
->numberOfFiles(min: 1)
// or max only:
->numberOfFiles(max: 10)
// or min and max:
->numberOfFiles(min: 1, max: 10);
use Tobento\App\Crud\Field;
Field\FileSource::new(
name: 'file',
// you may set a label, otherwise name is used:
label: 'File',
);
Field\FileSource::new('image')
->folder(path: 'shop/products')
// or using a callable:
->folder(static function(): string {
return sprintf('product/%s/%s/', date('Y'), date('m'));
});
Field\FileSource::new('image')
->allowedExtensions('jpg', 'png')
// you may set max file size in KB:
->maxFileSizeInKb(1000); // or null unlimited (default)
use Tobento\App\Media\Upload\ValidatorInterface;
use Tobento\App\Media\Upload\Validator;
Field\FileSource::new('image')
->validator(static function(): ValidatorInterface {
return new Validator(
allowedExtensions: ['jpg'],
strictFilenameCharacters: true,
maxFilenameLength: 200,
maxFileSizeInKb: 2000,
);
// or create your custom validator implementing the ValidatorInterface!
});
use Tobento\App\Media\FileStorage\FileWriter;
use Tobento\App\Media\FileStorage\FileWriterInterface;
use Tobento\App\Media\FileStorage\Writer;
use Tobento\App\Media\Image\ImageProcessor;
use Tobento\Service\FileStorage\StorageInterface;
Field\FileSource::new('image')
->fileWriter(static function(StorageInterface $storage): FileWriterInterface {
return new FileWriter(
storage: $storage,
filenames: FileWriter::ALNUM, // RENAME, ALNUM, KEEP
duplicates: FileWriter::RENAME, // RENAME, OVERWRITE, DENY
folders: FileWriter::ALNUM, // or KEEP
folderDepthLimit: 5,
writers: [
new Writer\ImageWriter(
imageProcessor: new ImageProcessor(
actions: [
'orientate' => [],
'resize' => ['width' => 2000],
],
),
),
new Writer\SvgSanitizerWriter(),
],
);
});
use Tobento\Service\Picture\DefinitionInterface;
use Tobento\Service\Picture\Definition\ArrayDefinition;
Field\FileSource::new('image')
->picture(definition: [
'img' => [
'src' => [120],
'loading' => 'lazy',
],
'sources' => [
[
'srcset' => [
'' => [120],
],
'type' => 'image/webp',
],
],
])
// or you may use a definition object implementing the DefinitionInterface:
->picture(definition: new ArrayDefinition('product-image', [
'img' => [
'src' => [120],
'loading' => 'lazy',
],
'sources' => [
[
'srcset' => [
'' => [120],
],
'type' => 'image/webp',
],
],
]))
// or you may disable it, showing just the path:
->picture(definition: null)
// You may queue the picture generation:
->pictureQueue(true);
'listeners' => [
\Tobento\App\Media\Event\ImageEdited::class => [
[\Tobento\App\Crud\Listener\ClearGeneratedPicture::class, ['definition' => 'crud-file-src']],
// you add more when using different definitions:
[\Tobento\App\Crud\Listener\ClearGeneratedPicture::class, ['definition' => 'product-image']],
],
],
use Tobento\App\Crud\Field;
Field\Items::new('prices')
// you may group the fields
->group('Prices') // set before defining fields!
// define the fields per item:
->fields(
Field\Text::new('price_net', 'Price Net')
->type('number')
->attributes(['step' => 'any'])
->validate('decimal'),
Field\Text::new('price_gross', 'Price Gross')
->type('number')
->attributes(['step' => 'any'])
->validate('decimal'),
)
// you may display items as card layout:
->displayAsCard()
// you may restrict items:
->validate('
use Tobento\App\Crud\Field;
Field\Options::new(
name: 'categories',
// you may set a label, otherwise name is used:
label: 'Categories',
);
use Tobento\Service\Repository\RepositoryInterface;
Field\Options::new('categories')
->repository(CategoriesRepository::class) // class-string|RepositoryInterface
// you may change the limit of the searchable options to be displayed:
->limit(15); // default is 25
// you may change the column value to be stored:
->storeColumn('sku') // 'id' is default
// you may change the search columns:
->searchColumns('title', 'sku'); // 'title' is default
use Tobento\App\Crud\Field;
use Tobento\Service\View\ViewInterface;
Field\Options::new('categories')
->toOption(function(object $item, ViewInterface $view, Field\Options $options): Field\Option {
return new Field\Option(
value: (string)$item->get('id'),
text: (string)$item->get('title'),
);
});
use Tobento\App\Crud\Field;
use Tobento\Service\View\ViewInterface;
Field\Options::new('categories')
->toOption(function(object $item, ViewInterface $view, Field\Options $options): Field\Option {
return (new Field\Option(value: (string)$item->get('id')))
->text((string)$item->get('title'))
->text((string)$item->get('sku'))
->html('html'); // must be escaped!
});
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field;
use Tobento\App\Crud\Field\FieldInterface;
Field\Options::new('categories')
->selected(value: ['2', '5'], action: 'create')
// or using a closure (additional parameters are resolved by autowiring):
->selected(
value: function (ActionInterface $action, FieldInterface $field): null|array {
return ['2', '5'];
//return null; // if none is selected
},
action: 'edit',
)
use Tobento\App\Crud\Field;
Field\PrimaryId::new(name: 'id', label: 'ID');
use Tobento\App\Crud\Field;
Field\Radios::new(
name: 'colors',
// you may set a label, otherwise name is used:
label: 'Colors',
);
Field\Radios::new('colors')
// specify the options using an array:
->options(['blue' => 'Blue', 'red' => 'Red'])
// or using a closure (parameters are resolved by autowiring):
->options(fn(ColorsRepository $repo): array => $repo->findColors());
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field;
use Tobento\App\Crud\Field\FieldInterface;
Field\Radios::new('colors')
->selected(value: 'blue', action: 'create')
// or using a closure (additional parameters are resolved by autowiring):
->selected(
value: function (ActionInterface $action, FieldInterface $field): null|string {
return 'blue';
//return null; // if none is selected
},
action: 'edit',
)
use Tobento\App\Crud\Field;
Field\Radios::new('colors')->attributes(['class' => 'name']);
Field\Radios::new('colors')
->displayInline();
Field\Radios::new('colors')
->validate('
use Tobento\App\Crud\Field;
Field\Select::new(
name: 'colors',
// you may set a label, otherwise name is used:
label: 'Colors',
);
Field\Select::new('colors')
// specify the options using an array:
->options(['blue' => 'Blue', 'red' => 'Red'])
// or using a closure (parameters are resolved by autowiring):
->options(fn(ColorsRepository $repo): array => $repo->findColors());
Field\Select::new('colors')
// specify the options using an array:
->options(['blue' => 'Blue', 'red' => 'Red'])
->emptyOption(value: 'none', label: '---');
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field;
use Tobento\App\Crud\Field\FieldInterface;
Field\Select::new('colors')
->selected(value: 'value', action: 'create')
// or using a closure (additional parameters are resolved by autowiring):
->selected(
value: function (ActionInterface $action, FieldInterface $field): null|string|array {
return ['blue', 'red']; // if multiple selection
//return 'blue'; // if single selection
//return null; // if none is selected
},
action: 'edit',
)
use Tobento\App\Crud\Field;
Field\Select::new('colors')->attributes(['multiple', 'size' => '10']);
use Tobento\App\Crud\Field;
Field\Slug::new(
name: 'slug',
// you may set a label, otherwise name is used:
label: 'SLUG',
);
use Tobento\App\Crud\Field;
Field\Slug::new('slug')->fromField('title');
use Tobento\App\Crud\Field;
use Tobento\Service\Slugifier\SlugifierInterface;
use Tobento\Service\Slugifier\SlugifiersInterface;
// using slugifier name:
Field\Slug::new('slug')->slugifier('crud');
// 'crud' is set as default but fallsback to default as not defined in slugging config
// using object:
Field\Slug::new('slug')->slugifier(new Slugifier());
// using closure:
Field\Slug::new('slug')
->slugifier(function (SlugifiersInterface $slugifiers): SlugifierInterface {
return $slugifiers->get('custom');
});
use Tobento\App\Crud\Field;
Field\Slug::new('slug')->uniqueSlugs(false);
use Tobento\App\Crud\Field;
Field\Slug::new(name: 'title')->attributes(['data-foo' => 'value']);
use Tobento\App\Crud\Field;
Field\Slug::new('slug')
->fromField('title')
->translatable()
->readonly(true, action: 'edit|update');
use Tobento\App\Crud\Field;
Field\Text::new(
name: 'title',
// you may set a label, otherwise name is used:
label: 'TITLE',
);
use Tobento\App\Crud\Field;
Field\Text::new(name: 'email')->type('email');
use Tobento\App\Crud\Field;
Field\Text::new(name: 'title')->value('Lorem');
// you may pass an array of values if your field is translatable:
Field\Text::new(name: 'title')
->translatable()
->value(['en' => 'Lorem', 'de' => 'Lorem ipsum']);
use Tobento\App\Crud\Field;
Field\Text::new(name: 'title')->defaultValue('Lorem');
// you may pass an array of values if your field is translatable:
Field\Text::new(name: 'title')
->translatable()
->defaultValue(['en' => 'Lorem', 'de' => 'Lorem ipsum']);
use Tobento\App\Crud\Field;
Field\Text::new(name: 'title')->attributes(['data-foo' => 'value']);
use Tobento\App\Crud\Field;
Field\Textarea::new(
name: 'title',
// you may set a label, otherwise name is used:
label: 'TITLE',
);
use Tobento\App\Crud\Field;
Field\Textarea::new(name: 'title')->attributes(['rows' => '5']);
use Tobento\App\Crud\Field;
Field\TextEditor::new(
name: 'desc',
// you may set a label, otherwise name is used:
label: 'Description',
);
use Tobento\Service\Repository\Storage\Column\Text;
use Tobento\Service\Repository\Storage\Column\ColumnsInterface;
use Tobento\Service\Repository\Storage\StorageRepository;
use function Tobento\App\HtmlSanitizer\sanitizeHtml;
class ExampleRepository extends StorageRepository
{
protected function configureColumns(): iterable|ColumnsInterface
{
return [
// ...
Column\Text::new('desc', type: 'text')
// as there might be data stored before, we clean the html on reading:
->read(fn (string $value): string => sanitizeHtml($value))
// clean the html on writing:
->write(fn (string $value): string => sanitizeHtml($value))
// ...
];
}
}
<?= $view->sanitizeHtml(html: $html)
use Tobento\App\Crud\Field;
Field\Value::new(
name: 'title',
// you may set a label, otherwise name is used:
label: 'TITLE',
);
use Tobento\App\Crud\Field;
Field\Value::new(name: 'title')->value('Lorem');
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
//...
Field\Text::new(name: 'sku')
// disabled on index action:
->indexable(false)
// disabled on create and store action:
->creatable(false)
// disabled on edit, update, delete and any bulk actions such as bulk-edit and bulk-delete action:
->editable(false)
// disabled on show action:
->showable(false),
//...
];
}
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
yield Field\PrimaryId::new('id');
if (in_array($action->name(), ['create', 'store'])) {
yield Field\Text::new(name: 'sku');
}
yield Field\Text::new(name: 'title');
}
use Tobento\App\Crud\Action\ActionsInterface;
use Tobento\App\Crud\Action;
use Tobento\App\Crud\Field;
protected function configureActions(): iterable|ActionsInterface
{
return [
Action\Index::new(title: 'Products')
->setFields(new Field\Fields(
Field\PrimaryId::new('id'),
Field\Text::new(name: 'sku'),
)),
];
}
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'sku')
// used for all actions:
->validate('
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'sku')
->validate('
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'title')
->translatable(),
];
}
use Tobento\Service\Repository\Storage\Column;
use Tobento\Service\Repository\Storage\Column\ColumnsInterface;
protected function configureColumns(): iterable|ColumnsInterface
{
return [
//...
Column\Translatable::new(name: 'title'),
];
}
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'foo')
->storable(false),
];
}
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'foo')
->readonly()
// or you may set only for specific actions:
->readonly(action: 'edit|update')
// or you may use a closure (parameters are resolved by autowiring):
->readonly(
readonly: function (ActionInterface $action, FieldInterface $field): bool {
return true;
},
action: 'edit|update'
)
->disabled()
// or you may set only for specific actions:
->disabled(action: 'edit|update')
// or you may use a closure (parameters are resolved by autowiring):
->disabled(
disabled: function (ActionInterface $action, FieldInterface $field): bool {
return true;
},
action: 'edit|update'
)
];
}
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'foo')->group('Name'),
Field\Text::new(name: 'bar')->group('Name'),
Field\Text::new(name: 'baz')->group('Another Name'),
];
}
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'foo')
->
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'foo')
->optionalText('optional ...')
// same as:
->optionalText(text: 'optional ...', action: 'create|edit')
// or using different text per action:
->optionalText(text: 'optional ...', action: 'edit'),
];
}
use Tobento\App\Crud\Action\ActionInterface;
use Tobento\App\Crud\Field\FieldsInterface;
use Tobento\App\Crud\Field;
protected function configureFields(ActionInterface $action): iterable|FieldsInterface
{
return [
Field\Text::new(name: 'foo')
->infoText('Some info ...')
// same as:
->infoText(text: 'Some info ...', action: 'create|edit')
// or using different text per action:
->infoText(text: 'Some info ...', action: 'edit'),
];
}
file
app/config/file_storage.php
app/config/event.php
app/config/slugging.php
app/config/slugging.php
app/config/slugging.php
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.