anik / laravel-backpack-extension
Class based imperative approach for Laravel backpack/crud
Requires
- php: ^8.0
- backpack/crud: ^5.0
Requires (Dev)
- orchestra/testbench: ^6.0|^7.5
README
anik/laravel-backpack-extension
In the table view of backpack/crud list operation, if a column exists in database table, it's by default searchable and orderable unless instructed otherwise. Also, to add a column or field to the operation, it is required to pass an array. In PHP, array keys are case-sensitive and this make it tedious when adding a field or column. This package allows class based imperative approach over passing the array. So no more tedious, repetitive typing game matching the exact array keys. BE FORGETFUL sometimes? 🤷
Documentation
Installation
To install the package, run
composer require anik/laravel-backpack-extension
Usage
Extending controller
Instead of extending the \Backpack\CRUD\app\Http\Controllers\CrudController
,
use \Anik\LaravelBackpack\Extension\Controllers\CrudController
in your controllers.
Adding Column
To instantiate a Column
new \Anik\LaravelBackpack\Extension\Columns\Column(string $name, [?string $label = null])
\Anik\LaravelBackpack\Extension\Columns\Column::create(string $name, [?string $label = null])
\Anik\LaravelBackpack\Extension\Columns\Column::ID([?string $label = null], [?string $name = null])
- To create an ID type column
To add the column(s) from the controller
- For a single column,
$this->registerColumn($column)
- For multiple columns,
$this->registerColumns($columns)
By default, all Column instances are not orderable and not searchable.
Example
use Anik\LaravelBackpack\Extension\Columns\Column; use Anik\LaravelBackpack\Extension\Controllers\CrudController; class AccountCrudController extends CrudController { public function setupListOperation () { $columns = [ Column::ID()->orderable(), // Only this column will be orderable Column::create('email')->searchable(), // Makes this field searchable Column::create('name')->searchable( fn ($q, $column, $term) => strlen($term = trim($term)) >= 3 ? $q->where('name', 'LIKE', sprintf('%%%s%%', $term)) : null) ), // Also makes this field searchable with custom logic ]; $this->registerColumns($columns); $this->registerColumn((new Column('is_verified', 'Verified'))->setType('boolean')); } }
Available methods
orderable()
- Make the column orderablesetOrderable(bool $orderable)
searchable()
- Make the column searchablesetSearchLogic(bool|Closure $logic)
- Make the column searchable with logic or not searchablesetKey(string $key)
setType(string $type)
- Set column typesetEntity(bool|string $entity)
setAttribute(string $attribute)
setModel(string $model)
setPriority(int $priority)
shouldNotEscape()
- Do not escape the value in the view{!! $value !!}
setEscaped(bool $escape)
setValue(mixed $value)
setLimit(int $limit)
setDefault(mixed $default)
setPrefix(mixed $prefix)
setSuffix(mixed $prefix)
setWrapper(mixed $wrapper, bool $mergeRecursive = false)
setOptions(mixed $options, bool $mergeRecursive = false)
isExportOnlyField()
isNotExportOnlyField()
shouldBeVisibleInTable()
shouldNotBeVisibleInTable()
shouldBeVisibleInModal()
shouldNotBeVisibleInModal()
isTableColumn()
isNotTableColumn()
setTableColumn(bool $tableColumn)
related(Relation $relation, bool $mergeRecursive = false)
- Check Relation section
Note
Column class uses \Anik\LaravelBackpack\Extension\Extensions\Attributable
trait.
Check Attributable section.
Adding Field
To instantiate a Field
new \Anik\LaravelBackpack\Extension\Fields\Field(string $name, [?string $label = null])
\Anik\LaravelBackpack\Extension\Fields\Field::create(string $name, [?string $label = null])
Or to create an OptionalField
new \Anik\LaravelBackpack\Extension\Fields\OptionalField(string $name, [?string $label = null])
\Anik\LaravelBackpack\Extension\Fields\OptionalField::create(string $name, [?string $label = null])
To add the field(s) from the controller
- For a single field,
$this->registerField($field)
- For multiple fields,
$this->registerFields($fields)
By default, all the Field instances are required.
Example
use Anik\LaravelBackpack\Extension\Controllers\CrudController; use Anik\LaravelBackpack\Extension\Fields\Field; class AccountCrudController extends CrudController { public function setupCreateOperation () { $fields = [ Field::create('email')->setType('email'), Field::create('is_admin')->checkbox(), ]; $this->registerFields($fields); $this->registerField((new Field('csrf_token'))->hidden()); } }
Available methods
required()
- Make field requiredoptional()
- Make field optionalsetType(string $type)
- Set field typerelated(Relation $relation, bool $mergeRecursive = false)
- Check Relation sectionsetRelationType(string $type)
setEntity(bool|string $entity)
setAttribute(mixed $attribute)
setModel(string $model)
setBaseModel(string $baseModel)
showAsterisk()
setShowAsterisk(mixed $value)
allowsMultiple()
setMultiple(mixed $multiple)
setPivot(mixed $pivot)
setSubfields(mixed $subfields)
setParentFieldName(mixed $value)
setPrefix(string $prefix)
setSuffix(string $suffix)
setDefault(mixed $default)
setValue(mixed $value)
setHint(string $hint)
setInline(mixed $inline)
hidden()
- Make hidden type fieldcheckbox()
- Make checkbox type fieldradio()
- Make radio type fieldnumber()
- Make number type fieldpassword()
- Make password type fieldsetPlaceholder(string $placeholder)
isReadOnly()
- Make field readonlyisDisabled()
- Make field disabledsetClass(string $class)
- Set field element css classsetAttributes(array $attributes, bool $mergeRecursive = true)
- Add additional attribute for field elementsetWrapper(mixed $value, bool $mergeRecursive = true)
setFake(bool $fake)
setStoresIn(string $storesIn)
setOptions(mixed $options, bool $mergeRecursive = true)
allowsNull()
setAllowsNull(bool $allowNull)
setTab(string $tab)
Note
Field class uses \Anik\LaravelBackpack\Extension\Extensions\Attributable
trait.
Check Attributable section.
Adding Filter
To instantiate a Filter
new \Anik\LaravelBackpack\Extension\Filters\Filter(string $name, [?string $label = null])
\Anik\LaravelBackpack\Extension\Filters\Filter::create(string $name, [?string $label = null])
\Anik\LaravelBackpack\Extension\Filters\AjaxFilter::create(string $name, [?string $label = null], [?string $url = null], [?string $method = null])
To add the filters(s) from the controller
- For a single filter,
$this->registerFilter($filter)
- For multiple filters,
$this->registerFilters($filters)
Example
use Anik\LaravelBackpack\Extension\Controllers\CrudController; use Anik\LaravelBackpack\Extension\Filters\Filter; use Anik\LaravelBackpack\Extension\Filters\AjaxFilter; class AccountCrudController extends CrudController { public function setupListOperation () { $filters = [ Filter::create('status') ->setValues([1 => 'Draft', 2 => 'Pending', 3 => 'Published',]) ->setLogic(fn($status) => $this->crud->query->where('status', $status)), AjaxFilter::create('user_id'), ]; $this->registerFilters($filters); $this->registerFilter((new Filter('is_deleted'))->setValues([0, 1])); } }
Available methods
setType(string $type)
- Set filter typesetViewNamespace(string $namespace)
setPlaceholder(string $placeholder)
setValues(string|array|callable $values)
setLogic(callable $logic)
setFallbackLogic(callable $fallbackLogic)
Note
Filter class uses \Anik\LaravelBackpack\Extension\Extensions\Attributable
trait.
Check Attributable section.
Adding Widget
To instantiate a Widget
new \Anik\LaravelBackpack\Extension\Widgets\Widget([?string $type = null], [?string $name = null], [?string $section = null])
\Anik\LaravelBackpack\Extension\Widgets\Script::create(string $src, [?string $name = null])
\Anik\LaravelBackpack\Extension\Widgets\Style::create(string $href, [?string $name = null])
new \Anik\LaravelBackpack\Extension\Widgets\Hidden([?string $type = null], [?string $name = null], [?string $section = null])
To add the widget(s) from the controller
- For a single widget,
$this->registerWidget($widget)
- For multiple widgets,
$this->registerWidgets($widgets)
Example
use Anik\LaravelBackpack\Extension\Controllers\CrudController; use Anik\LaravelBackpack\Extension\Widgets\Script; use Anik\LaravelBackpack\Extension\Widgets\Style; use Anik\LaravelBackpack\Extension\Widgets\Widget; class AccountCrudController extends CrudController { public function setupListOperation () { $widgets = [ Script::create('assets/js/common.js'), Style::create('assets/css/common.css'), ]; $this->registerWidgets($widgets); $this->registerWidget((new Widget('script'))->setContent('assets/js/another_common.js')); } }
Available methods
-
setSection(string $section)
-
setContent(mixed $content)
-
setViewNamespace(string $namespace)
-
shouldBeHidden()
-
shouldBeFirst()
-
shouldBeLast()
-
Script::setSrc(string $src)
-
Script::setStack(string $stack)
-
Style::setRel(string $rel)
-
Style::setHref(string $href)
-
Style::setStack(string $stack)
Note
Widget class uses \Anik\LaravelBackpack\Extension\Extensions\Attributable
trait.
Check Attributable section.
Relation
If a Column or Field points to an Eloquent relationship, then you can
use Anik\LaravelBackpack\Extension\Relations\Relation
.
To instantiate a Relation
new \Anik\LaravelBackpack\Extension\Relations\Relation(stirng $type, string $method, [?string $attribute = null])
new \Anik\LaravelBackpack\Extension\Relations\CustomRelation::create(string $type, string $method, [?string $attribute = null])
new \Anik\LaravelBackpack\Extension\Relations\BelongsTo::create(string $method, [?string $attribute = null])
new \Anik\LaravelBackpack\Extension\Relations\HasOne::create(string $method, [?string $attribute = null])
new \Anik\LaravelBackpack\Extension\Relations\BelongsTo::BelongsToMany(string $method, [?string $attribute = null])
new \Anik\LaravelBackpack\Extension\Relations\HasOne::HasMany(string $method, [?string $attribute = null])
Parameters:
$method
- The method name of the relationship in the eloquent model.$attribute
- The field/column/attribute of the related eloquent model.$type
- Used by backpack to pick the view to show the value calculated from the relationship.
The package provides 4 relations out-of-the-box which uses the type suggested by Backpack.
HasOne
,BelongsTo
- Type: selectHasMany
,BelongsToMany
- Type: select_multiple
If you want a customized Relationship, you can use \Anik\LaravelBackpack\Extension\Relations\CustomRelation
.
Example
use Anik\LaravelBackpack\Extension\Columns\Column; use Anik\LaravelBackpack\Extension\Controllers\CrudController; use Anik\LaravelBackpack\Extension\Fields\Field; use Anik\LaravelBackpack\Extension\Relations\BelongsTo; use Anik\LaravelBackpack\Extension\Relations\HasMany; class AccountCrudController extends CrudController { public function setupListOperation () { $columns = [ // Other columns Column::create('phone')->related(HasMany::create('phones', 'number')), ]; $this->registerColumns($columns); } public function setupCreateOperation() { $fields = [ // Other fields Field::create('country_id', 'Country')->related(BelongsTo::create('country', 'name')), ]; $this->registerFields($fields); } }
Available methods
setValueResolver(Closure $resolver)
- Set a closure which will be responsible to calculate the value for field/column
Note
Only CustomRelation class uses \Anik\LaravelBackpack\Extension\Extensions\Attributable
trait.
Check Attributable section.
Attributable trait
The Attributable trait allows classes to save and retrieve attributes in array format. Classes that use the trait will have access to the following methods.
-
addAttribute(string $key, mixed $value, bool $mergeRecursive = false)
-
addAttributes(array $attributes, bool $mergeRecursive = false)
-
unset(string $key)
-
toArray(): array
-
$mergeRecursive
indicates to if merge should be done using array_merge vs array_merge_recursive. -
addAttribute
,addAttributes
,unset
methods allows dot notation based keys when adding or unsetting values.
Example
use Anik\LaravelBackpack\Extension\Fields\Field; $field = Field::create('name'); $field->addAttribute('attributes.readonly', 'readonly'); // ['attributes' => ['readonly' => 'readonly']] // $field->addAttribute('attributes.disabled', 'disabled'); // w/o the parameter [mergeRecursive: true] -> ['attributes' => ['disabled' => 'disabled']] $field->addAttribute('attributes.disabled', 'disabled', true); // ['attributes' => ['readonly' => 'readonly', 'disabled' => 'disabled']] $field->addAttributes(['wrapper.class' => 'col-md-12'], true); $field->addAttributes(['wrapper' => ['another' => ['key' => 'value']]], true); // $field->addAttributes(['wrapper.another.key' => 'value'], true); // Alternative implementation of the above line /** * STRUCTURE: $field->toArray(); * * [ * 'attributes' => [ * 'readonly' => 'readonly', * 'disabled' => 'disabled' * ], * 'wrapper' => [ * 'class' => 'col-md-12', * 'another' => [ * 'key' => 'value' * ] * ] * ] */ $field->unset('wrapper.another'); /** * STRUCTURE: $field->toArray(); * * [ * 'attributes' => [ * 'readonly' => 'readonly', * 'disabled' => 'disabled' * ], * 'wrapper' => [ * 'class' => 'col-md-12' * ] * ] */