AgentStack
SKILL verified MIT Self-run

Concrete Dashboard Crud

skill-macareuxdigital-concretecms-skills-concrete-dashboard-crud · by MacareuxDigital

This skill provides instructions and best practices for implementing a CRUD (Create, Read, Update, Delete) interface for Doctrine entities within the Concrete CMS Dashboard, following standard core patterns.

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-macareuxdigital-concretecms-skills-concrete-dashboard-crud

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Concrete Dashboard Crud? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Concrete CMS Dashboard CRUD Implementation

This skill provides instructions and best practices for implementing a CRUD (Create, Read, Update, Delete) interface for Doctrine entities within the Concrete CMS Dashboard, following the standard core patterns.

Architecture Overview

A standard CRUD interface in Concrete CMS Dashboard consists of several layers:

  1. Doctrine Entity: The data model.
  2. Search Infrastructure:
  • ItemList: Extends \Concrete\Core\Search\ItemList\Database\ItemList. Handles querying the database.
  • SearchProvider: Extends \Concrete\Core\Search\Provider\AbstractSearchProvider. Provides the search configuration.
  • ColumnSet: Extends \Concrete\Core\Search\ColumnSet\ColumnSet. Defines table columns.
  • Result: Extends \Concrete\Core\Search\Result\Result. Handles result rendering.
  1. Dashboard Page Controller: Extends DashboardPageController. Manages actions (view, add, edit, submit, delete).
  2. UI Elements:
  • Single Page View: The main template.
  • Search Header: An element for the search bar.
  • Header Menu: An element for the "Add" button and pagination settings.

1. Search Infrastructure

ItemList

Always extend \Concrete\Core\Search\ItemList\Database\ItemList. Do not use EntityItemList as it is rarely used in core and less flexible.

namespace YourNamespace\Search\ItemList;

use Concrete\Core\Search\ItemList\Database\ItemList;
use Concrete\Core\Search\Pagination\PaginationProviderInterface;
use Pagerfanta\Doctrine\DBAL\QueryAdapter;

class YourEntityList extends ItemList implements PaginationProviderInterface
{
    protected function createQuery()
    {
        $this->query->select('e.id')->from('YourEntityTable', 'e');
        return $this->query;
    }

    public function getResult($queryRow)
    {
        $em = \ORM::entityManager();
        return $em->find('YourNamespace\Entity\YourEntity', $queryRow['id']);
    }

    public function getPaginationAdapter()
    {
        return new QueryAdapter($this->deliverQueryObject());
    }
    
    public function filterByKeywords($keywords)
    {
        $this->query->andWhere($this->query->expr()->like('e.name', $this->query->createNamedParameter('%' . $keywords . '%')));
    }
}

SearchProvider

namespace YourNamespace\Search\SearchProvider;

use Concrete\Core\Search\Provider\AbstractSearchProvider;
use YourNamespace\Search\ItemList\YourEntityList;
use YourNamespace\Search\ColumnSet\YourEntityColumnSet;

class YourEntitySearchProvider extends AbstractSearchProvider
{
    public function getCustomAttributeKeys() { return []; }
    public function getBaseColumnSet() { return new YourEntityColumnSet(); }
    public function getCurrentColumnSet() { return new YourEntityColumnSet(); }
    public function getDefaultColumnSet() { return new YourEntityColumnSet(); }
    
    public function getItemList()
    {
        return new YourEntityList();
    }
}

2. Dashboard Controller

The controller should manage the search state and pass necessary variables to elements.

public function view()
{
    $provider = $this->app->make(YourEntitySearchProvider::class);
    $list = $provider->getItemList();

    if ($this->request->query->has('keywords')) {
        $list->filterByKeywords($this->request->query->get('keywords'));
    }

    $itemsPerPage = $this->request->query->get('itemsPerPage', 10);
    $list->setItemsPerPage($itemsPerPage);

    $result = $provider->createSearchResultObject($provider->getDefaultColumnSet(), $list);
    
    $headerMenu = $this->app->make(ElementManager::class)->get('your_package/search/menu', 'your_package_handle');
    $headerMenu->set('result', $result);
    $headerMenu->set('itemsPerPage', $itemsPerPage);
    $headerMenu->set('itemsPerPageOptions', $provider->getItemsPerPageOptions());
    $headerMenu->set('urlHelper', $this->app->make('helper/url'));

    $headerSearch = $this->app->make(ElementManager::class)->get('your_package/search/search', 'your_package_handle');
    $headerSearch->set('headerSearchAction', $this->action('view'));

    $this->set('result', $result);
    $this->set('headerMenu', $headerMenu);
    $this->set('headerSearch', $headerSearch);
}

3. UI Implementation

Delete Confirmation

To implement a standard Concrete CMS delete confirmation dialog, use ConcreteAlert.confirm() in your search results table.

Controller

In your view() method, ensure the deletion action URL is available.

$this->set('deleteAction', $this->action('delete'));
View Template

Add a hidden form or a data-modal attribute containing the confirmation form for each row.

action('edit', $item->getItem()->getId()) ?>">
    getColumns() as $column) { ?>
        getColumnValue() ?>
    
    
        action('delete', $item->getItem()->getId()) . '">'
            . app('helper/validation/token')->output('delete', true)
            . t('Are you sure you want to delete this item?')
            . '';
        ?>
        " 
                onclick="ccm_deleteItem(this)">
            
        
    

    var ccm_deleteItem = function(elem) {
        var modal = elem.getAttribute('data-modal');
        ConcreteAlert.confirm(modal, function() {
            var submitButton = document.querySelector('.ui-dialog button[data-dialog-action]');
            submitButton.disabled = true;
            var modalForm = document.querySelector('#ccm-popup-confirmation form');
            modalForm.submit();
        }, 'btn-danger', '');
    };

Search Element (search.php)

Use app('helper/form') to get the form helper.


    ">
        
            text('keywords', ['placeholder' => t('Search')]) ?>
            
        
    

View Template

Use full-width content and standard table classes.


    
        
            
                getColumns() as $column) { ?>
                    getColumnStyleClass() ?>">
                        getColumnSortURL()) ?>">getColumnTitle() ?>
                    
                
            
        
        
            getItems() as $item) { ?>
                action('edit', $item->getItem()->getId()) ?>">
                    getColumns() as $column) { ?>
                        getColumnValue() ?>
                    
                
            
        
    
    
        getPagination()->renderView('dashboard') ?>
    

Best Practices

  • CSRF Protection: Always use $this->token->validate('action_name') in submit and delete methods.
  • Deletion Safety: Before deleting an entity, check for references in other tables and add a descriptive error if deletion is blocked.
  • Redirect after Post: Always return a redirect after successful submit or delete.
  • Coding Standards: Run concrete/bin/concrete c5:phpcs fix {path} on your PHP classes.
  • Element Manager: Use ElementManager to load header search and menu elements instead of creating new Element objects manually.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.