Skip to content

Commit

Permalink
Merge pull request #1 from heimrichhannot/feature/custom_routes
Browse files Browse the repository at this point in the history
Add custom route feature
  • Loading branch information
koertho authored Nov 6, 2024
2 parents cd1eaef + 66f0027 commit 3916b6a
Show file tree
Hide file tree
Showing 19 changed files with 368 additions and 74 deletions.
16 changes: 16 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Context**
Contao version:
Bundle version:
PHP version:

**Description**
<!-- Please describe the issue and provide reproduction steps (in a clean contao installation). If you report an error, please provide the full error message (ideally a screenshot of the error page in dev mode) -->
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to this project will be documented in this file.

## [1.3.0] - 2024-11-05
- Added: custom routes for ajax requests
- Changed: modernized bundle structure

## [1.2.2] - 2022-03-20
- Fixed: issues with php 8
-
Expand Down
69 changes: 65 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ For visualization the javascript library [DataTables](https://github.com/DataTab
- sort the table
- support for ajax reloading data using datatables -> currently only working for contao models since SQL-commands like LIMIT are used

### Technical instructions
## Technical instructions

#### Usage as a widget in a dca field
### Usage as a widget in a dca field

Use the inputType "listWidget" for your field.

Expand Down Expand Up @@ -51,7 +51,7 @@ Use the inputType "listWidget" for your field.
]
```

#### Usage in a module
### Usage in a module

Add the following code e.g. in the generate() method of your BackendModule:

Expand Down Expand Up @@ -85,7 +85,68 @@ ListWidget::addToTemplate($this->Template, static::$arrListOptions);

Copy the content of list_widget.html5 into your module's template.

#### Example load_items_callback
### Using ajax with custom routes

Since version 1.3 it is possible to use custom routes for ajax requests to have more control or freedom over the returned data.
Use `ListWidgetContext` class as helper to process the request and return a `ListWidgetResponse` object.

```php
# /contao/dca/tl_custom.php
$GLOBALS['TL_DCA']['tl_custom']['fields']['people'] = [
'inputType' => 'listWidget',
'eval' => [
'tl_class' => 'long clr',
'listWidget' => [
'ajax' => true,
'ajaxConfig' => [
'route' => 'app_list_widget_people_list',
],
'table' => 'tl_custom',
],
],
];

# /src/Controller/PeopleController.php

use Contao\CoreBundle\Framework\ContaoFramework;
use Doctrine\Common\Collections\Criteria;
use HeimrichHannot\ListWidgetBundle\Controller\ListWidgetContext;
use HeimrichHannot\ListWidgetBundle\Controller\ListWidgetResponse;
use App\People\PeopleFinder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

#[Route('/app/list_widget/people_list', name: 'app_list_widget_people_list', defaults: ['_scope' => 'backend'])]
class PeopleController
{
public function __construct(
private readonly ContaoFramework $framework,
private readonly PeopleFinder $people,
) {}

public function __invoke(Request $request): ListWidgetResponse
{
$this->framework->initialize();
try {
$context = ListWidgetContext::createFromRequest($request);
} catch (\Exception $e) {
return new ListWidgetResponse(1, 0, 0, [], $e->getMessage());
}

$countTotal = $this->people->countByList($context->id);
$fields = ['id', 'email', 'firstname', 'lastname'];

$criteria = new Criteria();
$context->applySearchToCriteria($criteria,$fields);
$countFiltered = $this->people->countByList($context->id, $criteria);
$context->applyListConfigToCriteria($criteria, $fields);
$people = $this->people->findByList($context->id, $fields, $criteria);
return $context->createResponse($countTotal, $countFiltered, $people);
}
}
```

### Example load_items_callback

Here you can see an example for overriding the core behavior of loadItems():

Expand Down
10 changes: 6 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@
"issues":"https://github.com/heimrichhannot/contao-list-widget-bundle/issues"
},
"require": {
"php": "^7.2 || ^8.0",
"php": "^8.1",
"ext-json": "*",
"contao/core-bundle": "^4.4",
"symfony/http-kernel": "^3.4 || ^4.0 || ^5.0",
"ausi/slug-generator": "^1.0",
"contao/core-bundle": "^4.13",
"heimrichhannot/contao-ajax-bundle": "~1.0",
"heimrichhannot/contao-request-bundle": "^1.1.1",
"heimrichhannot/contao-utils-bundle": "^2.225",
"heimrichhannot/datatables": "^1.10",
"heimrichhannot/datatables-additional": "^1.0"
"heimrichhannot/datatables-additional": "^1.0",
"symfony/http-foundation": "^5.4 || ^6.4 || ^7.0",
"symfony/http-kernel": "^5.4 || ^6.4 || ^7.0"
},
"replace": {
"heimrichhannot/contao-list_widget": "*"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
/**
* Backend form fields
*/
$GLOBALS['BE_FFL']['listWidget'] = ListWidget::class;
$GLOBALS['BE_FFL'][ListWidget::TYPE] = ListWidget::class;
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 1 addition & 4 deletions src/ContaoManager/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
* @license http://www.gnu.org/licences/lgpl-3.0.html LGPL
*/


namespace HeimrichHannot\ListWidgetBundle\ContaoManager;


use Contao\CoreBundle\ContaoCoreBundle;
use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
Expand All @@ -20,12 +18,11 @@

class Plugin implements BundlePluginInterface
{

public function getBundles(ParserInterface $parser): array
{
return [
BundleConfig::create(HeimrichHannotListWidgetBundle::class)->setLoadAfter([
ContaoCoreBundle::class
ContaoCoreBundle::class,
]),
];
}
Expand Down
88 changes: 88 additions & 0 deletions src/Controller/ListWidgetContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace HeimrichHannot\ListWidgetBundle\Controller;

use Ausi\SlugGenerator\SlugGenerator;
use Ausi\SlugGenerator\SlugOptions;
use Contao\Controller;
use Doctrine\Common\Collections\Criteria;
use HeimrichHannot\ListWidgetBundle\Widget\ListWidget;
use Symfony\Component\HttpFoundation\Request;

class ListWidgetContext
{
public function __construct(
public readonly string $table,
public readonly string $field,
public readonly string $id,
public readonly array $fieldConfig,
public readonly Request $request,
) {
}

public static function createFromRequest(Request $request): ListWidgetContext
{
$table = $request->query->get('table');
$field = $request->query->get('scope');

if (!$table || !$field) {
throw new \Exception('Missing required parameters.');
}

Controller::loadDataContainer($table);

$fieldConfig = $GLOBALS['TL_DCA'][$table]['fields'][$field] ?? false;

if (!$fieldConfig) {
throw new \Exception('Invalid field type.');
}

if (ListWidget::TYPE !== $fieldConfig['inputType']) {
throw new \Exception('Invalid field type.');
}

$id = $request->query->get('id');

return new ListWidgetContext($table, $field, $id, $fieldConfig, $request);
}

public function applySearchToCriteria(Criteria $criteria, array $fields): void
{
$slugger = new SlugGenerator((new SlugOptions())->setValidChars('A-Za-z0-9'));
if (!empty($this->request->query->get('search')['value'])) {
foreach ($fields as $field) {
$criteria->orWhere(Criteria::expr()->contains($field, $slugger->generate($this->request->query->get('search')['value'])));
}
}
}

public function applyListConfigToCriteria(Criteria $criteria, array $fields): void
{
$criteria->setMaxResults($this->request->query->get('length'));
$criteria->setFirstResult($this->request->query->get('start', 0));

if (!empty($this->request->query->get('order'))) {
foreach ($this->request->query->get('order') as $order) {
$criteria->orderBy([
$fields[$order['column']] => $order['dir'],
]);
}
}
}

public function adjustDataResultStructure(array &$data): void
{
array_walk($data, function (&$date) {
$date = array_map(fn($value) => [
'value' => $value,
], array_values($date));
});
}

public function createResponse(int $countTotal, int $countFiltered, array $data): ListWidgetResponse
{
$this->adjustDataResultStructure($data);

return new ListWidgetResponse($this->request->query->get('draw'), $countTotal, $countFiltered, $data);
}
}
28 changes: 28 additions & 0 deletions src/Controller/ListWidgetResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace HeimrichHannot\ListWidgetBundle\Controller;

use Symfony\Component\HttpFoundation\JsonResponse;

class ListWidgetResponse extends JsonResponse
{
public function __construct(int $draw, int $recordsTotal, int $recordsFiltered, array $data = null, ?string $error = null, int $status = 200, array $headers = [], bool $json = false)
{
$data = [
'result' => [
'data' => [
'draw' => $draw,
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $data,
],
],
];

if ($error) {
$data['result']['error'] = $error;
}

parent::__construct($data, $status, $headers, $json);
}
}
7 changes: 4 additions & 3 deletions src/HeimrichHannotListWidgetBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
* @license http://www.gnu.org/licences/lgpl-3.0.html LGPL
*/


namespace HeimrichHannot\ListWidgetBundle;


use Symfony\Component\HttpKernel\Bundle\Bundle;

class HeimrichHannotListWidgetBundle extends Bundle
{

public function getPath()
{
return \dirname(__DIR__);
}
}
Loading

0 comments on commit 3916b6a

Please sign in to comment.