Craft CMS Stack Exchange is a question and answer site for administrators, end users, developers and designers for Craft CMS. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm running the ElementAPI but would like to take advantage of some functionality provided by another plugin, to return these results in the API. Is this possible? Should all functions available in the main templates be available to the ElementAPI?

To be specific about what I'm trying to achieve--I'm trying to access the Imager plugin to handle optimised transformations of an uploaded image. Here's how the code looks in a regular Twig template

{% set transformedImage = craft.imager.transformImage(image, { width: 200 }) %}
<img src="{{ transformedImage.url }}">

And my attempt to do this inside elementapi.php is:

$sizerOptions = array( 'width' => '202' )
transformedImage = craft()->imager->transformImage( $asset->url, $sizerOptions );

My PHP is not brilliant so I may just be doing something silly here, but wondering if it's my fault, or if I'm attempting to do something impossible!

share|improve this question
up vote 4 down vote accepted

Your approach is technically correct – the problem is that craft()->imager->transformImage() requires two additional parameters; $transformDefaults and $configOverrides.

You can read more about these parameters and the transformImage method in the plugin's official docs, but it's not super important. The two parameters can actually be null, which means the following should work:

$sizerOptions = array('width' => 202);
$transformedImage = craft()->imager->transformImage($asset, $sizerOptions, null, null);

Note that while Imager does work with URLs, if you do have an AssetFileModel (i.e. $asset) it's actually better to feed that to transformImage() because then Imager will handle automatic cache breaking etc. for you (e.g. if you replace the image in Craft later on) – so my advice is to use transformImage($asset), not transformImage($asset->url), whenever possible.

Finally, in case you're wondering why the below works for templates:

{% set transformedImage = craft.imager.transformImage(image, transform) %}

...when you need the additional two parameters when you do the same thing in PHP, here's a quick explanation of what's going on:

Plugins in Craft are made up of several different components – among them, Services and Variables.

You can think of Services and Variables as two different APIs for a plugin – one is only made available to PHP (Services) and the other is only made available to Twig/templates (Variables).

Craft makes plugin Services available in PHP by instantiating them and adding them to the global craft() object in the format craft()->serviceName. For many plugins, there'll only be a single service, usually named after the plugin itself (e.g. ImagerService, which is mapped to craft()->imager). There can be several though, e.g. Imager_AnotherService, which would be mapped to craft()->imager_another.

Unlike Services, plugins can only have a single Variables class, which is always named after the plugin (e.g. PluginNameVariable) and mapped to craft.pluginName. In Imager's case, this means that the ImagerVariable is mapped to craft.imager in Twig.

Whenever you call craft.imager.transformImage() in a template, you're actually interacting with a Variable class – in this case, ImagerVariable. When you call craft()->imager->transformImage() in PHP, you're interacting with the ImagerService class.

The fact that the ImagerVariable and ImagerService both have a method called transformImage is a common design pattern in Craft, where variable classes are used to "wrap" Services, in order to expose the latter for use in templates. Often (and this is true for Imager, as well), the Variable methods do little more than to call their namesake Service methods. Using the same names for Variable and Service methods isn't a requirement, though – just a "best practice" to ensure consistent plugin APIs for templates and PHP. Imager's author could've opted to call the Variable method transform, doTransform, transformy or basically anything else.

Finally – as I mentioned, Imager's Variable method transformImage basically just wraps the ImagerService->transformImage method – but with one important difference: The Variable method makes the two additional parameters ($transformDefaults and $configOverrides) optional. This is why you don't have to declare them when you use Imager in a template, but you do have to define them when you use it in PHP.

A fun trick is that while Craft doesn't expose Template Variable classes to PHP out of the box, there's nothing preventing you from using them "manually". For example, say you prefer the Imager's Template Variable API over its Service API; here's how you can use the Variable methods wherever you want in your PHP:

Craft::import('plugins.imager.variables.ImagerVariable');
$imager = new ImagerVariable();

$transformedImage = $imager->transformImage($asset, $sizerOptions);
share|improve this answer
    
Hey, I just wanted to say that this is one of the best answers I've ever had the pleasure to receive on SO! I was absolutely stunned when I read through it. I'd actually tried passing the 2 extra null parameters into the function at some point but I can only think that I must have had a mistake elsewhere when I tried that. This sorted me out perfectly and I really appreciate the thorough explanation! Thanks man! – Jon Lay Feb 5 at 11:11
    
Whoa, that's awesome to hear @JonLay – thanks! Made my weekend :) – Mats Mikkel Rummelhoff Feb 5 at 11:12

You cannot say for sure how the PHP API of a Craft plugin has to be by just looking at the Twig code. You have to reverse engineer it. If the Twig function you're calling from the template starts with craft.pluginHandle, like in this case:

{% set var = craft.imager.transformImage() %}

you're dealing with a plugin's template variable. If the function isn't namespaced like this or if you apply it to a Twig value with the pipe character, it is a Twig extension.

{% set var = myTwigFunction() %} or
{% set var = value|myTwigFilter %}

Ok, so I now looked up that transformImage function in imager/variables/imagerVariable.php. I already expected this, because you're lucky and Andre is an excellent developer, but you can see it is basically passing all the parameters to a plugin service craft()->imager->transformImage(). Have a look at that specific service class imager/services/ImagerService.php. All its public methods are accessible from anywhere in your PHP code, it's the plugin's API. So what you have posted is actually very close to what you need, you just have some smaller syntax errors and if you take a closer look at the method's declaration, you'll see that it expects 4 parameters.

public function transformImage($image, $transform, $transformDefaults, $configOverrides) { }

Using the template variable you only need to set the first two of them and sets a default for the latter. But as the service method doesn't do this for you (in this case), you have to set them.

I haven't tested this, and might be missing something, but this should be at least a good starting point to get it working from another plugin like the Element API:

$sizerOptions = array('width' => 202);
$transformedImage = craft()->imager->transformImage($asset->url, $sizerOptions, null, null);
share|improve this answer
    
Thanks for the answer! You were right, but Mats got there first with his quite amazingly details reply. Appreciate the help, however! :) – Jon Lay Feb 5 at 11:12

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.