Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dependency Injection] [Filter] Implement filter service abstraction for creating images #922

Merged
merged 28 commits into from Nov 27, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a1abe98
Removed deprecated factory_class and factory_method
rvanlaarhoven Aug 5, 2016
94facff
Merge branch 'patch-1' of https://github.com/rvanlaarhoven/LiipImagin…
alexwilson Oct 25, 2016
6d399f6
Merge branch 'rvanlaarhoven-patch-1' into 2.0
alexwilson Oct 25, 2016
210922c
correct example filter yml definitions in docs (fixes #818) and other…
robfrawley Oct 30, 2016
c8648ca
correct accidental regression introduced in prior commit
robfrawley Oct 30, 2016
f8aa614
Merge pull request #820 from liip/image-cache-manager-typo
lsmith77 Nov 3, 2016
6f00396
Merge branch 'master' into 2.0
robfrawley Nov 10, 2016
dba4474
added coveralls reporting to travis
robfrawley Nov 23, 2016
d8435eb
added coveralls reporting to travis
robfrawley Nov 24, 2016
c708b79
updated readme for branch
robfrawley Nov 24, 2016
32c0a1f
Split image filtering functionality from ImagineController to new Fil…
rpkamp May 5, 2017
58603f3
README displays usage of new FilterService instead of ImagineController
rpkamp May 5, 2017
6681c81
Use new FilterService in ResolveCacheCommand
rpkamp May 5, 2017
f774704
Use long array notation since PHP 5.3 is still supported in Travis
rpkamp May 5, 2017
4bce009
CS fixes of docblocks
rpkamp May 5, 2017
9bf0ce8
No longer use unused classes in ResolveCacheCommand
rpkamp May 5, 2017
5122ca8
Use short class names instead of FQCN in docblocks
rpkamp May 5, 2017
3c8bf52
Merge branch '2.0' of github.com:liip/LiipImagineBundle into image-se…
rpkamp May 8, 2017
b60debc
Use short array syntax now that PHP5.3 support was dropped
rpkamp May 8, 2017
d5abad7
Merge branch '2.0' of github.com:liip/LiipImagineBundle into image-se…
rpkamp May 10, 2017
05afc99
Use FilterService in ResolveCacheProcessor
rpkamp May 10, 2017
d897e54
Merge branch '2.0' of github.com:rpkamp/LiipImagineBundle into image-…
rpkamp May 20, 2017
a8cb68d
Create filtered image in getUrlOf... methods in FilterService
rpkamp May 20, 2017
fdd9302
Update README to reflect removal of FilterService::create... methods
rpkamp May 20, 2017
edd673e
Use short ternary syntax for logger in FilterService
rpkamp May 20, 2017
605190d
Merge filter service with new Async processing
rpkamp Oct 12, 2017
fe47041
No longer require @dev for enqueue-bundle
rpkamp Oct 12, 2017
270c250
Require enqueue-bundle ^0.7, not ^0.7.0
rpkamp Oct 12, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 7 additions & 15 deletions Command/ResolveCacheCommand.php
Expand Up @@ -14,6 +14,7 @@
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Liip\ImagineBundle\Imagine\Data\DataManager;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
use Liip\ImagineBundle\Service\FilterService;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -63,30 +64,21 @@ protected function execute(InputInterface $input, OutputInterface $output)
$paths = $input->getArgument('paths');
$filters = $input->getOption('filters');

/* @var FilterManager filterManager */
/* @var FilterManager $filterManager */
$filterManager = $this->getContainer()->get('liip_imagine.filter.manager');
/* @var CacheManager cacheManager */
$cacheManager = $this->getContainer()->get('liip_imagine.cache.manager');
/* @var DataManager dataManager */
$dataManager = $this->getContainer()->get('liip_imagine.data.manager');

/* @var FilterService $filterService */
$filterService = $this->getContainer()->get('liip_imagine.service.filter');

if (empty($filters)) {
$filters = array_keys($filterManager->getFilterConfiguration()->all());
}

foreach ($paths as $path) {
foreach ($filters as $filter) {
if (!$cacheManager->isStored($path, $filter)) {
$binary = $dataManager->find($filter, $path);

$cacheManager->store(
$filterManager->applyFilter($binary, $filter),
$path,
$filter
);
}
$filterService->createFilteredImage($path, $filter);

$output->writeln($cacheManager->resolve($path, $filter));
$output->writeln($filterService->getUrlOfFilteredImage($path, $filter));
}
}
}
Expand Down
155 changes: 53 additions & 102 deletions Controller/ImagineController.php
Expand Up @@ -14,11 +14,9 @@
use Imagine\Exception\RuntimeException;
use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException;
use Liip\ImagineBundle\Exception\Imagine\Filter\NonExistingFilterException;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Liip\ImagineBundle\Imagine\Cache\SignerInterface;
use Liip\ImagineBundle\Imagine\Data\DataManager;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
use Psr\Log\LoggerInterface;
use Liip\ImagineBundle\Service\FilterService;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
Expand All @@ -27,165 +25,118 @@
class ImagineController
{
/**
* @var DataManager
*/
protected $dataManager;

/**
* @var FilterManager
* @var FilterService
*/
protected $filterManager;
private $filterService;

/**
* @var CacheManager
* @var DataManager
*/
protected $cacheManager;
private $dataManager;

/**
* @var SignerInterface
*/
protected $signer;

/**
* @var LoggerInterface
*/
protected $logger;
private $signer;

/**
* @param FilterService $filterService
* @param DataManager $dataManager
* @param FilterManager $filterManager
* @param CacheManager $cacheManager
* @param SignerInterface $signer
*/
public function __construct(
DataManager $dataManager,
FilterManager $filterManager,
CacheManager $cacheManager,
SignerInterface $signer,
LoggerInterface $logger = null
) {
public function __construct(FilterService $filterService, DataManager $dataManager, SignerInterface $signer)
{
$this->filterService = $filterService;
$this->dataManager = $dataManager;
$this->filterManager = $filterManager;
$this->cacheManager = $cacheManager;
$this->signer = $signer;
$this->logger = $logger;
}

/**
* This action applies a given filter to a given image, optionally saves the image and outputs it to the browser at the same time.
* This action applies a given filter to a given image, saves the image and redirects the browser to the stored
* image.
*
* The resulting image is cached so subsequent requests will redirect to the cached image instead applying the
* filter and storing the image again.
*
* @param Request $request
* @param string $path
* @param string $filter
*
* @throws \RuntimeException
* @throws BadRequestHttpException
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*
* @return RedirectResponse
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function filterAction(Request $request, $path, $filter)
{
// decoding special characters and whitespaces from path obtained from url
$path = urldecode($path);
$resolver = $request->get('resolver');

try {
if (!$this->cacheManager->isStored($path, $filter, $resolver)) {
try {
$binary = $this->dataManager->find($filter, $path);
} catch (NotLoadableException $e) {
if ($defaultImageUrl = $this->dataManager->getDefaultImageUrl($filter)) {
return new RedirectResponse($defaultImageUrl);
}

throw new NotFoundHttpException('Source image could not be found', $e);
}

$this->cacheManager->store(
$this->filterManager->applyFilter($binary, $filter),
$path,
$filter,
$resolver
);
}
$this->filterService->createFilteredImage($path, $filter, $resolver);

return new RedirectResponse($this->cacheManager->resolve($path, $filter, $resolver), 301);
} catch (NonExistingFilterException $e) {
$message = sprintf('Could not locate filter "%s" for path "%s". Message was "%s"', $filter, $path, $e->getMessage());

if (null !== $this->logger) {
$this->logger->debug($message);
return new RedirectResponse($this->filterService->getUrlOfFilteredImage($path, $filter, $resolver), 301);
} catch (NotLoadableException $e) {
if ($this->dataManager->getDefaultImageUrl($filter) !== null) {
return new RedirectResponse($this->dataManager->getDefaultImageUrl($filter));
}

throw new NotFoundHttpException($message, $e);
throw new NotFoundHttpException(sprintf('Source image for path "%s" could not be found', $path));
} catch (NonExistingFilterException $e) {
throw new NotFoundHttpException(sprintf('Requested non-existing filter "%s"', $filter));
} catch (RuntimeException $e) {
throw new \RuntimeException(sprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', $path, $filter, $e->getMessage()), 0, $e);
}
}

/**
* This action applies a given filter to a given image, optionally saves the image and outputs it to the browser at the same time.
* This action applies a given filter -merged with additional runtime filters- to a given image, saves the image and
* redirects the browser to the stored image.
*
* The resulting image is cached so subsequent requests will redirect to the cached image instead applying the
* filter and storing the image again.
*
* @param Request $request
* @param string $hash
* @param string $path
* @param string $filter
*
* @throws \RuntimeException
* @throws BadRequestHttpException
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*
* @return RedirectResponse
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function filterRuntimeAction(Request $request, $hash, $path, $filter)
{
$resolver = $request->get('resolver');
$runtimeConfig = $request->query->get('filters', array());

try {
$filters = $request->query->get('filters', array());

if (!is_array($filters)) {
throw new NotFoundHttpException(sprintf('Filters must be an array. Value was "%s"', $filters));
}

if (true !== $this->signer->check($hash, $path, $filters)) {
throw new BadRequestHttpException(sprintf(
'Signed url does not pass the sign check for path "%s" and filter "%s" and runtime config %s',
$path,
$filter,
json_encode($filters)
));
}

try {
$binary = $this->dataManager->find($filter, $path);
} catch (NotLoadableException $e) {
if ($defaultImageUrl = $this->dataManager->getDefaultImageUrl($filter)) {
return new RedirectResponse($defaultImageUrl);
}

throw new NotFoundHttpException(sprintf('Source image could not be found for path "%s" and filter "%s"', $path, $filter), $e);
}

$rcPath = $this->cacheManager->getRuntimePath($path, $filters);
if (!is_array($runtimeConfig)) {
throw new NotFoundHttpException(sprintf('Filters must be an array. Value was "%s"', $runtimeConfig));
}

$this->cacheManager->store(
$this->filterManager->applyFilter($binary, $filter, array(
'filters' => $filters,
)),
$rcPath,
if (true !== $this->signer->check($hash, $path, $runtimeConfig)) {
throw new BadRequestHttpException(sprintf(
'Signed url does not pass the sign check for path "%s" and filter "%s" and runtime config %s',
$path,
$filter,
$resolver
);
json_encode($runtimeConfig)
));
}

return new RedirectResponse($this->cacheManager->resolve($rcPath, $filter, $resolver), 301);
} catch (NonExistingFilterException $e) {
$message = sprintf('Could not locate filter "%s" for path "%s". Message was "%s"', $filter, $hash.'/'.$path, $e->getMessage());
try {
$this->filterService->createFilteredImageWithRuntimeFilters($path, $filter, $runtimeConfig, $resolver);

if (null !== $this->logger) {
$this->logger->debug($message);
return new RedirectResponse($this->filterService->getUrlOfFilteredImageWithRuntimeFilters($path, $filter, $runtimeConfig, $resolver), 301);
} catch (NotLoadableException $e) {
if ($this->dataManager->getDefaultImageUrl($filter) !== null) {
return new RedirectResponse($this->dataManager->getDefaultImageUrl($filter));
}

throw new NotFoundHttpException($message, $e);
throw new NotFoundHttpException(sprintf('Source image for path "%s" could not be found', $path));
} catch (NonExistingFilterException $e) {
throw new NotFoundHttpException(sprintf('Requested non-existing filter "%s"', $filter));
} catch (RuntimeException $e) {
throw new \RuntimeException(sprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', $hash.'/'.$path, $filter, $e->getMessage()), 0, $e);
}
Expand Down
2 changes: 1 addition & 1 deletion Imagine/Data/DataManager.php
Expand Up @@ -149,7 +149,7 @@ public function find($filter, $path)
*
* @param string $filter
*
* @return string
* @return string|null
*/
public function getDefaultImageUrl($filter)
{
Expand Down
75 changes: 25 additions & 50 deletions README.md
Expand Up @@ -299,8 +299,8 @@ from our documentation.
## Use as a Service

If you need to use your defined "filter sets" from within your controller, you
can fetch this bundle's controller from the service container and handle
the response yourself.
can fetch this bundle's FilterService from the service container to do the heavy
lifting for you.

```php
<?php
Expand All @@ -309,30 +309,31 @@ class MyController extends Controller
{
public function indexAction()
{
/** @var ImagineController */
/** @var FilterService */
$imagine = $this
->container
->get('liip_imagine.controller');

/** @var RedirectResponse */
$imagemanagerResponse = $imagine
->filterAction(
$this->request, // http request
'uploads/foo.jpg', // original image you want to apply a filter to
'my_thumb' // filter defined in config.yml
);

/** @var CacheManager */
$cacheManager = $this
->container
->get('liip_imagine.cache.manager');

/** @var string */
$sourcePath = $cacheManager
->getBrowserPath(
'uploads/foo.jpg',
'my_thumb'
);
->get('liip_imagine.service.filter');

// 1) Simple filter, OR
$imagine->createFilteredImage('uploads/foo.jpg', 'my_thumb');
$resourcePath = $imagine->getUrlOfFilteredImage('uploads/foo.jpg', 'my_thumb');

// 2) Runtime configuration
$runtimeConfig = [
'thumbnail' => [
'size' => [200, 200]
],
];
$imagine->createFilteredImageWithRuntimeFilters(
'uploads/foo.jpg',
'my_thumb',
$runtimeConfig
);
$resourcePath = $imagine->getUrlOfFilteredImageWithRuntimeFilters(
'uploads/foo.jpg',
'my_thumb',
$runtimeConfig
);

// ..
}
Expand All @@ -341,32 +342,6 @@ class MyController extends Controller
?>
```

If you need to add more logic, the recommended solution is to either
extend `ImagineController.php` or use it as the basis for your own
implementation.

To use the controller in another service, you have to simulate a new request.

```php
<?php

/** @var ImagineController */
$imagine = $this
->container
->get('liip_imagine.controller');

/** @var Response */
$response = $imagine
->filterAction(
new Symfony\Component\HttpFoundation\Request(),
'uploads/foo.jpg',
'my_thumb'
);

?>
```


## Data Roots

By default, Symfony's `web/` directory is registered as a data root to load
Expand Down