Skip to content

Commit

Permalink
Draft of Contao controllers (see #1376).
Browse files Browse the repository at this point in the history
Description
-----------

Abstract controllers that implement the same features as the abstract `ContentElement` and `Module` classes did.

Commits
-------

73cfc90 Draft of Contao controllers
0a6dc71 Implemented classes attribute from content element and frontend module
f3d6796 Use correct model type and remove todos
983fad6 Added a forward renderer to inherit POST parameters etc.
5cdc39a CS
9059847 Use the translator service for labels
5523f84 Added ForwardFragmentRenderer description
43b042d Added interface to set fragment options
f8fdf36 Correctly name controller argument
5c68bf9 Allow to set fragment options in tag and enable error handling
b90c5d7 Move getResponse method to make model argument more explicit
8fdd0fb Added unit tests, reduced duplicate code and several bugfixes
2d45938 Fix the tests.
e0a6102 Fix the coding style.
  • Loading branch information
aschempp authored and leofeyer committed Jun 11, 2018
1 parent 2f6c65b commit 5b64c9e
Show file tree
Hide file tree
Showing 18 changed files with 719 additions and 8 deletions.
120 changes: 120 additions & 0 deletions src/Controller/AbstractFragmentController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

declare(strict_types=1);

/*
* This file is part of Contao.
*
* Copyright (c) 2005-2018 Leo Feyer
*
* @license LGPL-3.0+
*/

namespace Contao\CoreBundle\Controller;

use Contao\CoreBundle\Fragment\FragmentOptionsAwareInterface;
use Contao\FrontendTemplate;
use Contao\Model;
use Contao\StringUtil;
use Contao\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\Container;

abstract class AbstractFragmentController extends Controller implements FragmentOptionsAwareInterface
{
/**
* @var array
*/
protected $options = [];

/**
* @param array $options
*/
public function setFragmentOptions(array $options): void
{
$this->options = $options;
}

/**
* Creates a template by name or from customTpl in model.
*
* @param Model $model
* @param string $templateName
*
* @return Template
*/
protected function createTemplate(Model $model, string $templateName): Template
{
if ($model->customTpl) {
$templateName = $model->customTpl;
}

$template = $this->get('contao.framework')->createInstance(FrontendTemplate::class, [$templateName]);
$template->setData($model->row());

return $template;
}

/**
* Adds the headline to the template.
*
* @param Template $template
* @param string|array $headline
*/
protected function addHeadlineToTemplate(Template $template, $headline): void
{
$data = StringUtil::deserialize($headline);
$template->headline = \is_array($data) ? $data['value'] : $data;
$template->hl = \is_array($data) ? $data['unit'] : 'h1';
}

/**
* Adds the CSS ID and class to the template.
*
* @param Template $template
* @param string $templateName
* @param string|array $cssID
* @param array|null $classes
*/
protected function addCssAttributesToTemplate(Template $template, string $templateName, $cssID, array $classes = null): void
{
$data = StringUtil::deserialize($cssID, true);
$template->class = trim($templateName.' '.($data[1] ?? ''));
$template->cssID = !empty($data[0]) ? ' id="'.$data[0].'"' : '';

if (\is_array($classes)) {
$template->class .= ' '.implode(' ', $classes);
}
}

/**
* Adds the article section to the template.
*
* @param Template $template
* @param string $section
*/
protected function addSectionToTemplate(Template $template, string $section): void
{
$template->inColumn = $section;
}

/**
* Returns the type from the class name.
*
* @return string
*/
protected function getType(): string
{
if (isset($this->options['type'])) {
return $this->options['type'];
}

$className = ltrim(strrchr(static::class, '\\'), '\\');

if ('Controller' === substr($className, -10)) {
$className = substr($className, 0, -10);
}

return Container::underscore($className);
}
}
55 changes: 55 additions & 0 deletions src/Controller/ContentElement/AbstractContentElementController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

/*
* This file is part of Contao.
*
* Copyright (c) 2005-2018 Leo Feyer
*
* @license LGPL-3.0+
*/

namespace Contao\CoreBundle\Controller\ContentElement;

use Contao\ContentModel;
use Contao\CoreBundle\Controller\AbstractFragmentController;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

abstract class AbstractContentElementController extends AbstractFragmentController
{
/**
* Invokes the controller.
*
* @param Request $request
* @param ContentModel $model
* @param string $section
* @param array|null $classes
*
* @return Response
*/
public function __invoke(Request $request, ContentModel $model, string $section, array $classes = null)
{
$type = $this->getType();
$template = $this->createTemplate($model, 'ce_'.$type);

$this->addHeadlineToTemplate($template, $model->headline);
$this->addCssAttributesToTemplate($template, 'ce_'.$type, $model->cssID, $classes);
$this->addSectionToTemplate($template, $section);

return $this->getResponse($template, $model, $request);
}

/**
* Returns the response.
*
* @param Template|\stdClass $template
* @param ContentModel $model
* @param Request $request
*
* @return Response
*/
abstract protected function getResponse(Template $template, ContentModel $model, Request $request): Response;
}
86 changes: 86 additions & 0 deletions src/Controller/FrontendModule/AbstractFrontendModuleController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

/*
* This file is part of Contao.
*
* Copyright (c) 2005-2018 Leo Feyer
*
* @license LGPL-3.0+
*/

namespace Contao\CoreBundle\Controller\FrontendModule;

use Contao\BackendTemplate;
use Contao\CoreBundle\Controller\AbstractFragmentController;
use Contao\ModuleModel;
use Contao\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

abstract class AbstractFrontendModuleController extends AbstractFragmentController
{
/**
* Invokes the controller.
*
* @param Request $request
* @param ModuleModel $model
* @param string $section
* @param array|null $classes
*
* @return Response
*/
public function __invoke(Request $request, ModuleModel $model, string $section, array $classes = null)
{
if ($this->get('contao.routing.scope_matcher')->isBackendRequest($request)) {
return $this->getBackendWildcard($model);
}

$type = $this->getType();
$template = $this->createTemplate($model, 'mod_'.$type);

$this->addHeadlineToTemplate($template, $model->headline);
$this->addCssAttributesToTemplate($template, 'mod_'.$type, $model->cssID, $classes);
$this->addSectionToTemplate($template, $section);

return $this->getResponse($template, $model, $request);
}

/**
* Returns the back end wildcard.
*
* @param ModuleModel $module
*
* @return Response
*/
protected function getBackendWildcard(ModuleModel $module): Response
{
$href = $this->get('router')->generate(
'contao_backend',
['do' => 'themes', 'table' => 'tl_module', 'act' => 'edit', 'id' => $module->id]
);

$name = $this->get('translator')->trans('FMD.'.$this->getType().'.0', [], 'contao_modules');

/** @var BackendTemplate|object $template */
$template = new BackendTemplate('be_wildcard');
$template->wildcard = '### '.strtoupper($name).' ###';
$template->id = $module->id;
$template->link = $module->name;
$template->href = $href;

return $template->getResponse();
}

/**
* Returns the response.
*
* @param Template|\stdClass $template
* @param ModuleModel $model
* @param Request $request
*
* @return Response
*/
abstract protected function getResponse(Template $template, ModuleModel $model, Request $request): Response;
}
8 changes: 7 additions & 1 deletion src/DependencyInjection/Compiler/RegisterFragmentsPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
namespace Contao\CoreBundle\DependencyInjection\Compiler;

use Contao\CoreBundle\Fragment\FragmentConfig;
use Contao\CoreBundle\Fragment\FragmentOptionsAwareInterface;
use Contao\CoreBundle\Fragment\FragmentPreHandlerInterface;
use Contao\CoreBundle\Fragment\Reference\ContentElementReference;
use Contao\CoreBundle\Fragment\Reference\FrontendModuleReference;
Expand Down Expand Up @@ -77,6 +78,10 @@ protected function registerFragments(ContainerBuilder $container, string $tag):
$preHandlers[$identifier] = $reference;
}

if (is_a($definition->getClass(), FragmentOptionsAwareInterface::class, true)) {
$definition->addMethodCall('setFragmentOptions', [$attributes]);
}

$registry->addMethodCall('add', [$identifier, $config]);
$definition->addTag($tag, $attributes);
}
Expand All @@ -100,7 +105,8 @@ protected function getFragmentConfig(ContainerBuilder $container, Reference $ref
FragmentConfig::class,
[
$this->getControllerName($reference, $attributes),
$attributes['renderer'] ?? 'inline',
$attributes['renderer'] ?? 'forward',
array_merge(['ignore_errors' => false], $attributes['options'] ?? []),
]
);

Expand Down
43 changes: 43 additions & 0 deletions src/Fragment/ForwardFragmentRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

/*
* This file is part of Contao.
*
* Copyright (c) 2005-2018 Leo Feyer
*
* @license LGPL-3.0+
*/

namespace Contao\CoreBundle\Fragment;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;

/**
* Implements the forward rendering strategy.
*
* The default "inline" renderer creates a new, almost blank request object for
* each subrequest, which means a fragment controller will not get POST data or
* other main request configuration. Contrary to regular Symfony inline
* fragments, however, the Contao fragments are supposed to handle POST data.
*/
class ForwardFragmentRenderer extends InlineFragmentRenderer
{
/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'forward';
}

/**
* {@inheritdoc}
*/
protected function createSubRequest($uri, Request $request): Request
{
return $request->duplicate();
}
}
2 changes: 1 addition & 1 deletion src/Fragment/FragmentConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class FragmentConfig
*
* @see \Symfony\Component\HttpKernel\Fragment\FragmentHandler::render()
*/
public function __construct(string $controller, string $renderer = 'inline', array $options = [])
public function __construct(string $controller, string $renderer = 'forward', array $options = [])
{
$this->controller = $controller;
$this->renderer = $renderer;
Expand Down
23 changes: 23 additions & 0 deletions src/Fragment/FragmentOptionsAwareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

/*
* This file is part of Contao.
*
* Copyright (c) 2005-2018 Leo Feyer
*
* @license LGPL-3.0+
*/

namespace Contao\CoreBundle\Fragment;

interface FragmentOptionsAwareInterface
{
/**
* Sets the fragment options.
*
* @param array $options
*/
public function setFragmentOptions(array $options): void;
}
1 change: 1 addition & 0 deletions src/Fragment/Reference/ContentElementReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ public function __construct(ContentModel $model, string $section = 'main')

$this->attributes['contentModel'] = $model->id;
$this->attributes['section'] = $section;
$this->attributes['classes'] = $model->classes;
}
}
1 change: 1 addition & 0 deletions src/Fragment/Reference/FrontendModuleReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ public function __construct(ModuleModel $model, string $section = 'main')

$this->attributes['moduleModel'] = $model->id;
$this->attributes['section'] = $section;
$this->attributes['classes'] = $model->classes;
}
}

0 comments on commit 5b64c9e

Please sign in to comment.