Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Crudit Bundle Laravel Package

2lenet/crudit-bundle

Symfony bundle to rapidly build configurable CRUD back offices with SB Admin layout. Provides list views with pagination, sorting, actions, exports and batch ops, plus datasources, filters, menus, workflows, maps, markdown and Twig helpers.

View on GitHub
Deep Wiki
Context7

To create a crud, simply type this command in the console of your project:

php bin/console make:crudit

You will then be asked a series of questions :

  • the name of your Entity
  • the namespace of your Controller
  • if the command should also create filters related to your entity's fields

That's it! Several files have been created:

  • src/Crudit/Config/EntityCrudConfig.php
  • src/Controller/Crudit/EntityController.php
  • src/Form/EntityType.php
  • src/Crudit/Datasource/Filterset/EntityFilterSet.php
  • src/Crudit/Datasource/EntityDatasource.php

How to sort your list:

  public function getDefaultSort(): array
    {
        return [['numeroLot', 'ASC']];
    }

How to change the default sorting of your autocomplete route

public function getDefaultAutocompleteSort(): ?array
{
    return [['label', 'ASC']];
}

How to configure the choices of the number of items to display:

  public function getChoicesNbItems(): array
  {
      return [15, 30, 45, 60];
  }
  
  

How to configure cssClass for a field in a card:

Use setCardCssClass on a field to apply a CSS class to it in the card view.

Field::new('myField')->setCardCssClass('col-md-6');

How to add actions:

It is possible to add actions to lists, list items and shows. It is already possible to add action in a dropdown, adding ->setDropdown(true) to the action object.

  public function getListActions(): array
  {
      $res[] = ListAction::new(
          "action.export",
          $this->getPath(CrudConfigInterface::EXPORT),
          Icon::new("file-export")
      )
          ->setModal("[@LleCrudit](https://github.com/LleCrudit)/modal/_export.html.twig")
          ->setConfig(
              [
                  "export" => [Exporter::CSV, Exporter::EXCEL],
              ]
          );
          
      $res[] = ListAction::new(
          'action.import.csv',
          Path::new('import_lot_melange'),
          Icon::new('download')
      )->setDropdown(true);

      $res[] = ListAction::new(
          'action.batch.bons_livraisons',
          Path::new('create_bons_livraisons'),
          Icon::new('truck-loading')
      )->setIsBatch();

      return $res;
  }

If you want to add a role for your action:

  public function getListActions(): array
  {          
      $res[] = ListAction::new(
          'action.import.csv',
          (Path::new('import_lot_melange'))->setRole('ROLE_IMPORT'),
          Icon::new('download')
      )->setDropdown(true);

      return $res;
  }

How to display the action under certain conditions

You can add conditions to actions, using the resource or not, in order to decide whether to display the action based on the data or the request.

To do this, simply use setDisplayIf (for the data) or setDisplayIfByRequest (for the request):

$actions[CrudConfigInterface::ACTION_SHOW] = ItemAction::new(
    'action.show',
    $this->getPath(CrudConfigInterface::SHOW),
    Icon::new('search')
)
    ->setCssClass('btn btn-primary btn-sm crudit-action')
    ->setRole(sprintf('ROLE_%s_%s', $this->getName(), CrudConfigInterface::SHOW))
    ->setDisplayIf(fn(Resource $resource) => $resource->isActive())
    ->setDisplayIfByRequest(fn(Request $request) => $request->query->has('actif'));

You can configure editable conditions on lle\src\Dto\Field by adding

 Field::new()->setEditInPlace(true)->setEditableIf(callback) 

Write actions that will be executed in your crud

When we declare a new action in our page, we indicate the path of our method. This method must be in src/Controller/Crudit/EntityController.php

Configure the form to be displayed in the new and edit screens

In src/Form/EntityType.php. The form is a basic Symfony form. Several formtypes are available. See : Form types

Configure the filters in your list

In src/Crudit/Datasource/Filterset/EntityFilterSet.php. See Filters

Enable striped tables

To enable striped tables in the lists, you must add this scss :

.crudit-list {
    & > tbody > tr:nth-of-type(odd) {
        --#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-striped-bg);
        color: var(--#{$variable-prefix}table-striped-color);
    }
}

:warning: Don't forget to add your new crud to the menu in src/Crudit/CrudMenu/AppMenuProvider.php:

LinkElement::new(
    'menu.groupes',
    Path::new('app_crudit_contact_index'),
    Icon::new('/img/icons/contact.svg', Icon::TYPE_IMG),
    "ROLE_CONTACT_INDEX"
)

How to add totals on your list:

It is possible to add totals to your list by adding the getTotalsFields method to the CrudConfig file.

public function getTotalFields(): array
{
    return [
        'montantHt' => [
            'type' => CrudConfigInterface::SUM,
            'field' => Field::new('montantHt', 'currency'),
        ],
        'montantTtc' => [
            'type' => CrudConfigInterface::SUM,
            'field' => Field::new('montantTtc', 'currency'),
        ],
    ];
}

You can choose between 3 types of totals, AVERAGE, SUM and COUNT. To use them, use the constants defined in the CrudConfigInterface file.

For computed totals that combine several columns, use EXPRESSION and provide a raw DQL expression:

public function getTotalFields(): array
{
    return [
        'totalAmount' => [
            'type' => CrudConfigInterface::SUM,
            'field' => Field::new('totalAmount', 'currency'),
        ],
        'grossMargin' => [
            'type' => CrudConfigInterface::EXPRESSION,
            'expression' => 'SUM(root.totalAmount) - SUM(root.supplierCost)',
            'field' => Field::new('grossMargin', 'currency'),
        ],
    ];
}

EXPRESSION totals are also computed per group when withSubtotals() is used.

:warning: Don't forget to specify the type of your field, as Crudit is unable to determine this itself.

You can also use totals on your sublist by adding getSubListTotalFields method to the CruditConfig file.

Configure route for DoctrineEntityField

To configure route for DoctrineEntityField, you must set the route options:

Field::new('yourdoctrinentityfield', null, ['route' => 'your_route']);

Or:

Field::new('yourdoctrinentityfield')->setOptions(['route' => 'your_route']);

Configure autocomplete route for DoctrineEntityField

To configure autocomple route for DoctrineEntityField, you must set the setAutocompleteUrl method:

Field::new('yourdoctrinentityfield')->setAutocompleteUrl('your_autocomplete_url');

If you want to be able to select several items, you need to add the setMultiple method:

Field::new('yourdoctrinentityfield')->setAutocompleteUrl('your_autocomplete_url')->setMultiple(true);

Configure role for DoctrineEntityField

If you want to add a role for your DoctrineEntityField:

Field::new('yourdoctrinentityfield', null, ['route' => 'your_route', 'routeRole' => 'YOUR_ROLE']);

Or:

Field::new('yourdoctrinentityfield')->setOptions(['route' => 'your_route', 'routeRole' => 'YOUR_ROLE']);

How to refresh field on your list after using editInPlace:

It is possible to refresh the value of a field in a list/show/sublist after modifying one using the fieldsToUpdate method. To do this, you need to return an array containing each id of the element you want to refresh and the HTML code contained in that element.

The id of the element contain a part call 'yourfieldlabel', if the label of your field start with 'field.' or 'label.', Crudit automatically remove it. So if you've 'field.toto', 'yourefieldlabel' must be 'toto' but if you've 'text.toto' you'll have 'text.toto'.

public function fieldsToUpdate(int|string $id): array
{
    $result = $this->em->getRepository(YourEntity::class)->find($id);

    if (!$result) {
        return [];
    }

    return [
        'sublist-yourentity-' . $result->getId() . '-yourfieldid-yourfieldlabel' => $this->twig->render('the/template.html.twig', [
            'view' => [
                'field' => Field::new('yourfield')->setEditable('app_crudit_your_entity_editdata')
            ],
            'resource' => $result,
            'value' => $result->getYourField(),
            'options' => [
                "tableCssClass" => "text-end",
                'decimals' => '2',
                'decimal_separator' => ',',
                'thousands_separator' => ' ',
            ],
        ]),
    ];
}

:warning: If you refresh a field which is also an editInPlace, you need to configure the eipToUpdate method to re-enable the edit capability.

public function eipToUpdate(int|string $id): array
{
    $result = $this->em->getRepository(YourEntity::class)->find($id);

    if (!$result) {
        return [];
    }

    return [
        'sublist-yourentity-' . $result->getId() . '-yourfieldid-yourfieldlabel',
    ];
}

How to auto refresh the page:

It is possible to auto refresh the page for the list and/or the show using the getListAutoRefresh/getShowAutoRefresh methods. To do this, you need to return an integer that corresponds to the time interval (in seconds) between 2 refreshes.

public function getListAutoRefresh(): ?int
{
    return 60;
}

public function getShowAutoRefresh(): ?int
{
    return 60;
}

How to configure field groups in the show brick

If you have a lot of information to put in your show brick, you can divide it into several groups, which will create several expandable elements (open by default).

public function getFields(string $key): array
{
    switch ($key) {
        case CrudConfigInterface::SHOW:
            $fields = [
                'title.group1' => [
                    $field1,
                    $field2,
                    $field3,
                ],
                'title.group2' => [
                    $field4,
                    $field5,
                ],
            ];
            break;
    }
}

You can configure the number of field groups opened by default using the getShowNumberFieldGroupsOpened method.

public function getShowNumberFieldGroupsOpened(): ?int
{
    return 2;
}

How to group rows in your list (rupture)

It is possible to visually group list rows by a field value using setRuptGroup. Two levels are supported.

public function getFields(string $key): array
{
    switch ($key) {
        case CrudConfigInterface::INDEX:
            $fields = [
                Field::new('groupField')->setRuptGroup(1),
                Field::new('subGroupField')->setRuptGroup(2),
                Field::new('fieldA'),
                Field::new('fieldB'),
            ];
            break;
    }
}

Any sort configured on the list (user click or getDefaultSort) takes priority over the rupture sort by default. The rupture sort is appended after the existing sorts to guarantee consistent visual grouping within pages. See withRuptSortPriority() below if you need the rupture to always be the primary sort.

A group header row is always displayed, even when a group continues from the previous page.

:warning: Rupture fields must be database-mapped properties or Doctrine relations. Computed PHP methods are not supported.

How to group rows by a date part (year, month, day…)

When the rupture field is a date, use setRuptDateFormat to define the grouping granularity. This method accepts a PHP date format string and is used for SQL GROUP BY computation and for comparing successive rows to detect group changes.

Field::new('yourDateField')->setRuptGroup(1)->setRuptDateFormat('Y-m'),  // group by month
Field::new('yourDateField')->setRuptGroup(1)->setRuptDateFormat('Y'),    // group by year
Field::new('yourDateField')->setRuptGroup(1)->setRuptDateFormat('Y-m-d'),// group by day

Supported format characters: Y, y, m, n, d, j, H, h, i, s. Literal separators (-, /, , :) are passed through as-is.

To control how the date appears in the rupture header row, use setRuptDateDisplayFormat. It also accepts a PHP date format string and is applied only to the header label — it has no effect on SQL grouping or data cells.

Field::new('yourDateField')
    ->setRuptGroup(1)
    ->setRuptDateFormat('Y-m')           // group by month (SQL + row comparison)
    ->setRuptDateDisplayFormat('m/Y'),   // display as "06/2026" in the header

:warning: If setRuptDateDisplayFormat is not set, the header falls back to the field's normal rendered value.

How to customise the rupture header row

CSS class

Use setRuptCssClass to apply Bootstrap (or custom) CSS classes to the rupture header <tr>. The default is no extra class.

Field::new('groupField')->setRuptGroup(1)->setRuptCssClass('bg-info text-center'),

Show the field as a regular column too

By default the rupture field is hidden from the data rows (it only appears as a group header). Use setRuptHideFromList(false) to keep it visible as a normal column as well.

Field::new('groupField')->setRuptGroup(1)->setRuptHideFromList(false),

Label for null values

When the field value is null, the header displays "{field label} empty" using the translation key crudit.rupture.null_label. Override this with setRuptNullLabel:

Field::new('groupField')->setRuptGroup(1)->setRuptNullLabel('my.custom.translation.key'),

How to give the rupture sort priority over other sorts

By default, user-defined sorts (column clicks, getDefaultSort) take precedence and the rupture sort is appended after them. Use withRuptSortPriority() on the rupture field to invert this: the rupture becomes the primary sort and existing sorts apply within each group.

Field::new('orderDate')
    ->setRuptGroup(1)
    ->setRuptDateFormat('Y-m')
    ->withRuptSortPriority(),   // rupture is always the first ORDER BY

Without this option the rupture sort is appended last, so clicking a column header re-orders within the current groups but the groups themselves may not remain contiguous if the column sort conflicts with the rupture field.

How to add subtotals per rupture group

When getTotalFields is defined, you can also display subtotals for each level-1 rupture group by adding withSubtotals() to the rupture field.

Field::new('groupField')->setRuptGroup(1)->withSubtotals(),

The subtotals use the same fields defined in getTotalFields. They are computed via a single SQL GROUP BY query and are therefore accurate across all pages, not just the visible page.

A subtotal row is displayed at the end of each group. If a group spans multiple pages, the same global subtotal is repeated on each page.

:warning: withSubtotals() only works on level-1 rupture fields. Level-2 rupture groups do not have subtotals.

:warning: The datasource must extend AbstractDoctrineDatasource for subtotals to be available.

Complete example

Field::new('orderDate')
    ->setRuptGroup(1)
    ->setRuptDateFormat('Y-m')
    ->setRuptDateDisplayFormat('m/Y')
    ->setRuptHideFromList(false)
    ->setRuptCssClass('bg-info text-center')
    ->setRuptNullLabel('my.custom.null_key')   // optional
    ->withRuptSortPriority()                   // optional — rupture sorts before any other sort
    ->withSubtotals(),
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
bugban/php-sdk
littlerocket/job-queue-bundle
graham-campbell/flysystem
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
directorytree/opensearch-client
directorytree/opensearch-adapter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php