This bundle is a fork of AvalancheImagineBundle which provides easy image manipulation support for Symfony2. The goal of the fork is to make the code more extensible and as a result applicable for more use cases.
For more details on the reason for the fork see: avalanche123/AvalancheImagineBundle#25
For example with this bundle the following is possible:
<img src="{{ '/relative/path/to/image.jpg' | imagine_filter('thumbnail') }}" />
This will perform the transformation called thumbnail
, which you can define
to do a number of different things, such as resizing, cropping, drawing,
masking, etc.
This bundle integrates the standalone PHP "Imagine library".
To install this bundle, you'll need both the Imagine library and this bundle. Installation depends on how your project is setup:
Add the following lines to your deps
file
[Imagine]
git=http://github.com/avalanche123/Imagine.git
target=imagine
version=v0.2.0
[LiipImagineBundle]
git=http://github.com/liip/LiipImagineBundle.git
target=bundles/Liip/ImagineBundle
Next, update your vendors by running:
$ ./bin/vendors install
Add the following entries to your autoloader:
<?php
// app/autoload.php
$loader->registerNamespaces(array(
// ...
'Imagine' => __DIR__.'/../vendor/imagine/lib',
'Liip' => __DIR__.'/../vendor/bundles',
));
Finally, enable the bundle in the kernel:
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Liip\ImagineBundle\LiipImagineBundle(),
);
}
Finally, add the following to your routing file:
# app/config/routing.yml
_imagine:
resource: .
type: imagine
Congratulations! You're ready to rock your images!
This bundle works by configuring a set of filters and then applying those filters to images inside a template So, start by creating some sort of filter that you need to apply somewhere in your application. For example, suppose you want to thumbnail an image to a size of 120x90 pixels:
# app/config/config.yml
liip_imagine:
filter_sets:
my_thumb:
quality: 75
filters:
thumbnail: { size: [120, 90], mode: outbound }
You've now defined a filter set called my_thumb
that performs a thumbnail transformation.
We'll learn more about available transformations later, but for now, this
new filter can be used immediately in a template:
<img src="{{ '/relative/path/to/image.jpg' | imagine_filter('my_thumb') }}" />
Or if you're using PHP templates:
<img src="<?php $this['imagine']->filter('/relative/path/to/image.jpg', 'my_thumb') ?>" />
Behind the scenes, the bundles applies the filter(s) to the image on the first request and then caches the image to a similar path. On the next request, the cached image would be served directly from the file system.
In this example, the final rendered path would be something like
/media/cache/my_thumb/relative/path/to/image.jpg
. This is where Imagine
would save the filtered image file.
In order to get an absolute path to the image add another parameter with the value true:
<img src="{{ '/relative/path/to/image.jpg' | imagine_filter('my_thumb', true) }}" />
Or if you're using PHP templates:
<img src="<?php $this['imagine']->filter('/relative/path/to/image.jpg', 'my_thumb', true) ?>" />
Note: Using the dev
environment you might find that the images are not properly rendered when
using the template helper. This is likely caused by having intercept_redirect
enabled in your
application configuration. To ensure that the images are rendered disable this option:
web_profiler:
intercept_redirects: false
The default configuration for the bundle looks like this:
liip_imagine:
driver: gd
web_root: %kernel.root_dir%/../web
data_root: %liip_imagine.web_root%
cache_prefix: /media/cache
cache: web_path
cache_clearer: true
data_loader: filesystem
controller_action: liip_imagine.controller:filterAction
formats: []
filter_sets:
# Prototype
name:
path: ~
quality: 100
format: ~
cache: ~
data_loader: ~
controller_action: ~
filters:
# Prototype
name: []
There are several configuration options available:
-
web_root
- must be the absolute path to you application's web root. This is used to determine where to put generated image files, so that apache will pick them up before handing the request to Symfony2 next time they are requested.default:
%kernel.root_dir%/../web
-
data_root
- the absolute path to the location that original files should be sourced from. This option only changes the standard filesystem loader.default:
%kernel.root_dir%/../web
-
cache_prefix
- this is also used in the path for image generation, so as to not clutter your web root with cached images. For example by default, the images would be written to theweb/media/cache/
directory.default:
/media/cache
-
cache
- default cache resolverdefault: web_path (which means the standard web_path resolver is used)
-
cache_clearer
- Whether or not to clear the image cache when thekernel.cache_clearer
event occurs. This option doesn't have any effect in symfony < 2.1default: true
-
data_loader
- name of a custom data loaderdefault: filesystem (which means the standard filesystem loader is used)
-
controller_action
- name of the controller action to use in the route loaderdefault: liip_imagine.controller:filterAction
-
driver
- one of the three drivers:gd
,imagick
,gmagick
default:
gd
-
formats
- optional list of image formats to which images may be converted to. -
filter_sets
- specify the filter sets that you want to define and use
Each filter set that you specify has the following options:
filters
- determine the type of filter to be used (refer to Filters section for more information) and options that should be passed to the specific filter typepath
- used in place of the filter name to determine the path in combination with the globalcache_prefix
quality
- override the default quality of 100 for the generated imagescache
- override the default cache settingdata_loader
- override the default data loadercontroller_action
- override the default controller actionformat
- hardcodes the output format (aka the requested format is ignored)
The thumbnail filter, as the name implies, performs a thumbnail transformation on your image. Configuration looks like this:
liip_imagine:
filter_sets:
my_thumb:
filters:
thumbnail: { size: [120, 90], mode: outbound }
The mode
can be either outbound
or inset
.
There is also a option allow_upscale
(default: false).
The relative_resize
filter may be used to heighten
, widen
, increase
or
scale
an image with respect to its existing dimensions. These options directly
correspond to methods on Imagine's BoxInterface
.
Given an input image sized 50x40 (width, height), consider the following annotated configuration examples:
liip_imagine:
filter_sets:
my_heighten:
filters:
relative_resize: { heighten: 60 } # Transforms 50x40 to 75x60
my_widen:
filters:
relative_resize: { widen: 32 } # Transforms 50x40 to 32x26
my_increase:
filters:
relative_resize: { increase: 10 } # Transforms 50x40 to 60x50
my_widen:
filters:
relative_resize: { scale: 2.5 } # Transforms 50x40 to 125x100
The crop filter, as the name implies, performs a crop transformation on your image. Configuration looks like this:
liip_imagine:
filter_sets:
my_thumb:
filters:
crop: { start: [10, 20], size: [120, 90] }
The ImagineBundle allows you to load your own custom filter classes. The only requirement is that each filter loader implement the following interface:
Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface
To tell the bundle about your new filter loader, register it in the service
container and apply the liip_imagine.filter.loader
tag to it (example here in XML):
<service id="liip_imagine.filter.loader.my_custom_filter" class="Acme\ImagineBundle\Imagine\Filter\Loader\MyCustomFilterLoader">
<tag name="liip_imagine.filter.loader" loader="my_custom_filter" />
</service>
For more information on the service container, see the Symfony2 Service Container documentation.
You can now reference and use your custom filter when defining filter sets you'd like to apply in your configuration:
liip_imagine:
filter_sets:
my_special_style:
filters:
my_custom_filter: { }
For an example of a filter loader implementation, refer to
Liip\ImagineBundle\Imagine\Filter\Loader\ThumbnailFilterLoader
.
If you need to use the filters in a controller, you can just load ImagineController.php
controller as a service and handle the response:
class MyController extends Controller {
public function indexAction() {
// RedirectResponse object
$imagemanagerResponse = $this->container
->get('liip_imagine.controller')
->filterAction(
$this->getRequest(),
'uploads/foo.jpg', // original image you want to apply a filter to
'my_thumb' // filter defined in config.yml
);
// string to put directly in the "src" of the tag <img>
$srcPath = $imagemanagerResponse->headers->get('location');
}
}
In case you need to add more logic the recommended solution is to either extend ImagineController.php
controller or take the code from that controller as a basis for your own controller.
When your setup requires your source images to live outside the web root, or if that's just the way you roll,
you can override the DataLoader service and define a custom path, as the third argument, that replaces
%liip_imagine.web_root%
(example here in XML):
<service id="liip_imagine.data.loader.filesystem" class="%liip_imagine.data.loader.filesystem.class%">
<tag name="liip_imagine.data.loader" loader="filesystem" />
<argument type="service" id="liip_imagine" />
<argument>%liip_imagine.formats%</argument>
<argument>%kernel.root_dir%/data/uploads</argument>
</service>
One way to override a service is by redefining it in the services configuration file of your bundle. Another way would be by modifying the service definition from your bundle's Dependency Injection Extension:
$container->getDefinition('liip_imagine.data.loader.filesystem')
->replaceArgument(2, '%kernel.root_dir%/data/uploads');
The ImagineBundle allows you to add your custom image loader classes. The only requirement is that each data loader implement the following interface:
Liip\ImagineBundle\Imagine\Data\Loader\LoaderInterface
To tell the bundle about your new data loader, register it in the service
container and apply the liip_imagine.data.loader
tag to it (example here in XML):
<service id="acme_imagine.data.loader.my_custom" class="Acme\ImagineBundle\Imagine\Data\Loader\MyCustomDataLoader">
<tag name="liip_imagine.data.loader" loader="my_custom_data" />
<argument type="service" id="liip_imagine" />
<argument>%liip_imagine.formats%</argument>
</service>
For more information on the service container, see the Symfony2 Service Container documentation.
You can set your custom data loader by adding it to the your configuration as the new default loader as follows:
liip_imagine:
data_loader: my_custom_data
Alternatively you can only set the custom data loader for just a specific filter set:
liip_imagine:
filter_sets:
my_special_style:
data_loader: my_custom_data
filters:
my_custom_filter: { }
For an example of a data loader implementation, refer to
Liip\ImagineBundle\Imagine\Data\Loader\FileSystemLoader
.
The Liip\ImagineBundle\Imagine\Data\Loader\StreamLoader
allows to read images from any stream registered
thus allowing you to serve your images from literally anywhere.
The example service definition shows how to use a stream wrapped by the Gaufrette filesystem abstraction layer. In order to have this example working, you need to register the stream wrapper first, refer to the Gaufrette README on how to do this.
services:
liip_imagine.data.loader.stream.profile_photos:
class: "%liip_imagine.data.loader.stream.class%"
arguments:
- "@liip_imagine"
- 'gaufrette://profile_photos/'
tags:
- { name: 'liip_imagine.data.loader', loader: 'stream.profile_photos' }
Load your images from mongodb
liip_imagine:
filter_sets:
my_special_style:
data_loader: grid_fs
filters:
my_custom_filter: { }
Add loader to your services
<service id="liip_imagine.data.loader.grid_fs" class="Liip\ImagineBundle\Imagine\Data\Loader\GridFSLoader">
<tag name="liip_imagine.data.loader" loader="grid_fs" />
<argument type="service" id="liip_imagine" />
<argument type="service" id="doctrine.odm.mongodb.document_manager" />
<argument>Application\ImageBundle\Document\Image</argument>
</service>
Reference the image by its id
<img src="{{ image.id | imagine_filter('my_thumb') }}" />
You can extend a custom data loader to support virtually any file type using transformers.
A data tranformer is intended to transform a file before actually rendering it. You
can refer to Liip\ImagineBundle\Imagine\Data\Loader\ExtendedFileSystemLoader
and
to Liip\ImagineBundle\Imagine\Data\Transformer\PdfTransformer
as an example.
ExtendedFileSystemLoader extends FileSystemLoader and takes, as argument, an array of transformers. In the example, when a file with the pdf extension is passed to the data loader, PdfTransformer uses a php imagick object (injected via the service container) to extract the first page of the document and returns it to the data loader as a png image.
To tell the bundle about the transformers, you have to register them as services with the new loader:
services:
imagick_object:
class: Imagick
acme_custom_transformer:
class: Acme\ImagineBundle\Imagine\Data\Transformer\MyCustomTransformer
arguments:
- '@imagick_object'
custom_loader:
class: Acme\ImagineBundle\Imagine\Data\Loader\MyCustomDataLoader
tags:
- { name: liip_imagine.data.loader, loader: custom_data_loader }
arguments:
- '@liip_imagine'
- %liip_imagine.formats%
- %liip_imagine.data_root%
- [ '@acme_custom_transformer' ]
Now you can use your custom data loader, with its transformers, setting it as in the previous section.
The ImagineBundle allows you to add your custom cache resolver classes. The only requirement is that each cache resolver loader implement the following interface:
Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface
To tell the bundle about your new cache resolver, register it in the service
container and apply the liip_imagine.cache.resolver
tag to it (example here in XML):
<service id="acme_imagine.cache.resolver.my_custom" class="Acme\ImagineBundle\Imagine\Cache\Resolver\MyCustomCacheResolver">
<tag name="liip_imagine.cache.resolver" resolver="my_custom_cache" />
<argument type="service" id="filesystem" />
<argument type="service" id="router" />
<argument>%liip_imagine.web_root%</argument>
</service>
For more information on the service container, see the Symfony2 Service Container documentation.
You can set your custom cache reslover by adding it to the your configuration as the new default resolver as follows:
liip_imagine:
cache: my_custom_cache
Alternatively you can only set the custom cache resolver for just a specific filter set:
liip_imagine:
filter_sets:
my_special_style:
cache: my_custom_cache
filters:
my_custom_filter: { }
For an example of a cache resolver implementation, refer to
Liip\ImagineBundle\Imagine\Cache\Resolver\WebPathResolver
.
Custom cache resolver classes must implement the clear
method, at worst doing nothing.
When the console cache:clear
command is run, the clear method of all the registered cache
resolvers is automatically called.
The AmazonS3Resolver requires the aws-sdk-php.
You can add the SDK by adding those lines to your deps
file.
[aws-sdk]
git=git://github.com/amazonwebservices/aws-sdk-for-php.git
Afterwards, you only need to configure some information regarding your AWS account and the bucket.
parameters:
amazon_s3.key: 'your-aws-key'
amazon_s3.secret: 'your-aws-secret'
amazon_s3.bucket: 'your-bucket.example.com'
Now you can set up the services required:
services:
acme.amazon_s3:
class: AmazonS3
arguments:
-
key: %amazon_s3.key%
secret: %amazon_s3.secret%
# more S3 specific options, see \AmazonS3::__construct()
acme.imagine.cache.resolver.amazon_s3:
class: Liip\ImagineBundle\Imagine\Cache\Resolver\AmazonS3Resolver
arguments:
- "@acme.amazon_s3"
- "%amazon_s3.bucket%"
tags:
- { name: 'liip_imagine.cache.resolver', resolver: 'amazon_s3' }
Now you are ready to use the AmazonS3Resolver
by configuring the bundle.
The following example will configure the resolver is default.
liip_imagine:
cache: 'amazon_s3'
If you want to use other buckets for other images, simply alter the parameter names and create additional services!
With a custom data loader it is possible to dynamically modify the configuration that will
be applied to the image. Inside the controller you can access the FilterConfiguration
instance, dynamically adjust the filter configuration (for example based on information
associated with the image or whatever other logic you might want) and set it again.
A simple example showing how to change the filter configuration dynamically. This example is of course "bogus" since hardcoded values could just as well be set in the configuration but it illustrates the core idea.
public function filterAction(Request $request, $path, $filter)
{
$targetPath = $this->cacheManager->resolve($request, $path, $filter);
if ($targetPath instanceof Response) {
return $targetPath;
}
$image = $this->dataManager->find($filter, $path);
$filterConfig = $this->filterManager->getFilterConfiguration();
$config = $filterConfig->get($filter);
$config['filters']['thumbnail']['size'] = array(300, 100);
$filterConfig->set($filter, $config);
$response = $this->filterManager->get($request, $filter, $image, $path);
if ($targetPath) {
$response = $this->cacheManager->store($response, $targetPath, $filter);
}
return $response;
}