Skip to content

Instantly share code, notes, and snippets.

@mneuhaus
Last active August 29, 2015 14:05
Show Gist options
  • Save mneuhaus/6c183f5d84d094a90393 to your computer and use it in GitHub Desktop.
Save mneuhaus/6c183f5d84d094a90393 to your computer and use it in GitHub Desktop.

This gist is a collection of classes and configurations that enable to have an entity as a "persistent" context in multiple controllers.

  • ClientRoutePartHandler.php + Routes.yaml: these to handle the conversion of the route
  • ClientContextAware.php: identifies a controller to be ClientContextAware
  • ClientArgumentAspect.php: checks if the current controller ist ClientContextAware and adds the argument client to any url that is build without manual intervention
  • ClientContext.php: fetches the current client as a context, you can inject this class into anything where you might need the client to work with inside a ClientContextAware Controllercall.
  • Package.php: Injects the current Request into the ClientContext

Example

Given you have a Client with a name acme and a controller called ClientController that contains functions that all need a client to work. With these classes you can have urls like this:

http://foo.com/acme/client/index

That will automatically parse that /acme/ part into the matching client and you can inject the ClientContext into your ClientController, Services, ViewHelpers, etc to work with the current client, similar to the SecurityContext.

<?php
namespace My\Package\Controller;

use My\Package\Routing\ClientContext;
use Flowpack\Expose\Domain\Schema;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Mvc\Controller\ActionController;
use My\Package\Annotations as Foo;

/**
 *
 * @Foo\ClientContextAware
 */
class ClientController extends ActionController {
	/**
	 * @Flow\Inject
	 * @var ClientContext
	 */
	protected $clientContext;

	/**
	 */
	public function indexAction() {
		$this->clientContext->getClient();
		...
	}
}
<?php
namespace My\Package\Routing;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Mvc\ActionRequest;
use TYPO3\Flow\Reflection\ReflectionService;
/**
* @Flow\Aspect
*/
class ClientArgumentAspect {
/**
* @Flow\Inject
* @var \TYPO3\Flow\Object\ObjectManager
*/
protected $objectManager;
/**
* @Flow\Inject
* @var ReflectionService
*/
protected $reflectionService;
/**
* @param \TYPO3\Flow\AOP\JoinPointInterface $joinPoint
* @Flow\Before("method(TYPO3\Flow\Mvc\Routing\UriBuilder->uriFor())")
* @return void
*/
public function addEntityClassName(\TYPO3\Flow\AOP\JoinPointInterface $joinPoint) {
if ($this->isClientContextAwareController($joinPoint) === FALSE) {
return;
}
$request = $joinPoint->getProxy()->getRequest();
$controllerArguments = $joinPoint->getMethodArgument('controllerArguments');
if ($request->hasArgument('client') && isset($controllerArguments['client']) === FALSE) {
$controllerArguments['client'] = $request->getArgument('client');
$joinPoint->setMethodArgument('controllerArguments', $controllerArguments);
}
}
public function isClientContextAwareController($joinPoint) {
$request = $joinPoint->getProxy()->getRequest();
if (!$request instanceof ActionRequest) {
return FALSE;
}
$controllerName = $joinPoint->getMethodArgument('controllerName');
if ($controllerName === NULL) {
$controllerName = $request->getControllerName();
}
$packageKey = $joinPoint->getMethodArgument('packageKey');
if ($packageKey === NULL) {
$packageKey = $request->getControllerPackageKey();
}
$subPackageKey = $joinPoint->getMethodArgument('subPackageKey');
if ($subPackageKey === NULL) {
$subPackageKey = $request->getControllerSubPackageKey();
}
$possibleObjectName = '\@package\@subpackage\Controller\@controllerController';
$possibleObjectName = str_replace('@package', str_replace('.', '\\', $packageKey), $possibleObjectName);
$possibleObjectName = str_replace('@subpackage', $subPackageKey, $possibleObjectName);
$possibleObjectName = str_replace('@controller', $controllerName, $possibleObjectName);
$possibleObjectName = str_replace('\\\\', '\\', $possibleObjectName);
$controllerObjectName = $this->objectManager->getCaseSensitiveObjectName($possibleObjectName);
return $this->reflectionService->isClassAnnotatedWith($controllerObjectName, 'My\Package\Annotations\ClientContextAware');
}
}
<?php
namespace My\Package\Routing;
/* *
* This script belongs to the TYPO3 Flow package "Devis.Tool". *
* *
* */
use My\Package\Domain\Model\Client;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Mvc\ActionRequest;
use TYPO3\Flow\Persistence\PersistenceManagerInterface;
use TYPO3\Flow\Persistence\Repository;
/**
* @Flow\Scope("singleton")
*/
class ClientContext {
/**
* @var Client
*/
protected $client;
/**
* @var ActionRequest
*/
protected $currentRequest;
/**
* @Flow\Inject
* @var PersistenceManagerInterface
*/
protected $persistenceManager;
public function injectCurrentRequest($request) {
$this->currentRequest = $request;
}
public function getClient() {
if ($this->client === NULL && $this->currentRequest->hasArgument('client')) {
$this->client = $this->persistenceManager->getObjectByIdentifier($this->currentRequest->getArgument('client'), 'My\Package\Domain\Model\Client');
}
return $this->client;
}
}
?>
<?php
namespace My\Package\Annotations;
/* *
* This script belongs to the TYPO3 Flow framework. *
* *
* It is free software; you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License, either version 3 *
* of the License, or (at your option) any later version. *
* *
* The TYPO3 project - inspiring people to share! *
* */
/**
* @Annotation
* @Target("CLASS")
*/
final class ClientContextAware {
}
<?php
namespace My\Package\Routing;
use My\Package\Domain\Model\Client;
use My\Package\Domain\Repository\ClientRepository;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Persistence\PersistenceManagerInterface;
class ClientRoutePartHandler extends \TYPO3\Flow\Mvc\Routing\DynamicRoutePart {
/**
* @Flow\Inject
* @var ClientRepository
*/
protected $clientRepository;
/**
* @Flow\Inject
* @var PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* Checks whether the current URI section matches the configured RegEx pattern.
*
* @param string $requestPath value to match, the string to be checked
* @return boolean TRUE if value could be matched successfully, otherwise FALSE.
*/
protected function matchValue($requestPath) {
$client = $this->clientRepository->findOneBySlug($requestPath);
if (!$client instanceof Client) {
return FALSE;
}
$this->value = $this->persistenceManager->getIdentifierByObject($client);
return TRUE;
}
/**
* Checks whether the route part matches the configured RegEx pattern.
*
* @param string $value The route part (must be a string)
* @return boolean TRUE if value could be resolved successfully, otherwise FALSE.
*/
protected function resolveValue($value) {
if (is_string($value)) {
$value = $this->persistenceManager->getObjectByIdentifier($value, 'My\Package\Domain\Model\Client');
}
if (!$value instanceof Client) {
return FALSE;
}
$this->value = $value->getSlug();
return TRUE;
}
}
<?php
namespace CE\Devis;
/* *
* This script belongs to the TYPO3 Flow framework. *
* *
* It is free software; you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License, either version 3 *
* of the License, or (at your option) any later version. *
* *
* The TYPO3 project - inspiring people to share! *
* */
use TYPO3\Flow\Configuration\ConfigurationManager;
use TYPO3\Flow\Core\Bootstrap;
use TYPO3\Flow\Package\Package as BasePackage;
/**
* The TYPO3 Flow Package
*
*/
class Package extends BasePackage {
/**
* Invokes custom PHP code directly after the package manager has been initialized.
*
* @param \TYPO3\Flow\Core\Bootstrap $bootstrap The current bootstrap
* @return void
*/
public function boot(Bootstrap $bootstrap) {
$dispatcher = $bootstrap->getSignalSlotDispatcher();
$dispatcher->connect(
'TYPO3\Flow\Mvc\ActionRequest', 'requestDispatched',
'CE\Devis\Routing\ClientContext', 'injectCurrentRequest'
);
}
}
-
name: 'default with action and format'
uriPattern: '{client}/{@controller}/{@action}(.{@format})'
defaults:
'@package': 'My.Package'
'@subpackage': 'Frontend'
'@format': 'html'
routeParts:
'client':
handler: 'My\Package\Routing\ClientRoutePartHandler'
appendExceedingArguments: true
-
name: 'default'
uriPattern: '{client}/{@controller}(/{@action})'
defaults:
'@package': 'My.Package'
'@subpackage': 'Frontend'
'@action': 'index'
'@format': 'html'
routeParts:
'client':
handler: 'My\Package\Routing\ClientRoutePartHandler'
appendExceedingArguments: true
-
name: 'default with package'
uriPattern: '{client}'
defaults:
'@package': 'My.Package'
'@subpackage': 'Frontend'
'@controller': 'Standard'
'@action': 'index'
'@format': 'html'
routeParts:
'client':
handler: 'My\Package\Routing\ClientRoutePartHandler'
appendExceedingArguments: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment