Problem/Motivation
At the moment it is only possible to create an image derivative starting from a file URI. It would be good to have the possibility to do it from an already instantiated Image object. template_preprocess_image_style_preview() is e.g. getting an Image object, then handing over its source path to createDerivative() which just opens another Image instance for the same file. Also, in contrib you may want to create an Image programmatically and apply an image style to it without having to flush it to disk before.
Proposed resolution
Add two methods to ImageStyle, apply and applyAndSave. Final names of the methods to be decided (see comments #17.4, 21, 22, 23).
Remaining tasks
- decide on how to introduce the two new methods, new interface or no interface (see comment #25)
- decide on the methods' names (see comments #17.4, 21, 22, 23)
- draft change record
User interface changes
None
API changes
Two methods added.
Comments
Comment #1
mondrakePatch.
Comment #3
mondrakeFixed failure and changed a test to use createDerivativeFromImage() so that this method is tested explicilty
Comment #4
mondrakego bot
Comment #5
claudiu.cristeaGreat idea and looks good.
Not possible to inject?
Comment #6
mondrake#5 from a quick research it may be possible to implement
EntityHandlerInterfacetoImageStyle:itscreateInstancemethod should be able to get the container and pass to the constructor individual services. A bit likeContainerFactoryPluginInterfacefor plugins.But I'd leave that to a separate followup issue.
Comment #7
fietserwinYour idea is good, The current createDerivative() does too many things in 1 method, making it indeed hard to reuse functionality in unforeseen ways. However, I think that the new method should be named apply() (conforming with the image - toolkit - operation naming) or applyStyle() (conforming with applyEffect() on ImageEffect). I prefer the first, as IMO the latter should also be renamed to apply() (for reasons of consistency and DRY: do not repeat the name of the class on method names). This would be perfectly consistent as all apply() methods accept an ImageInterface as 1st argument (except on ImageInterface itself of course).
Regarding the comment in #5 and the answer in #6: I prefer to do it correct the 1st time, the patch will remain small and reviewable and as we are already in beta, structural/creational choices should be done correct by now.
Can we create a follow-up for ImageEfect?
Comment #8
mondrakeChanging to feature request and hence to 8.1.x queue.
Comment #9
mondrakeComment #10
mondrakeFor later 8.1.x discussions...
This patch:
file_uri_scheme(see also #2527726: Remove wrapper to procedural function in ImageStyle), other wrappers, adjusted PHPUnit tests, and changed deprecated functionsfile_uri_schemeanddrupal_dirnameto the new OO methods::createDerivativeFromImage()to two methods::applyAndSave, equivalent to ::createDerivativeFromImage(), and::applywhich just applies the effects to the Image object without IO::createDerivativein non-test code have been optimised using the new::applyAndSavemethod - since the image after saving has the relevant information 'fresh', there is no purpose to fetch the derivative image from the file system again just to get it. So I suggest to mark::createDerivativefor deprecation and keep it in 8 cycle for BC.::createDerivativein test-code: one I changed here to test::applyAndSave, the others can be removed here or in a follow upComment #11
mondrakeComment #12
mondrakeComment #13
mondrakeReroll.
Comment #14
claudiu.cristeaThis is a nice change, I agree.
Oh, very nice! This also deserves a 'performance' tag?
So, in this case $image is the original image, right? Then inside if ($success) {...} we'll send headers with info from the original image pretending that is the derivative?
Sorry, it's my fault from #5. We cannot do that because we are initialising all the services in constructor and maybe we don't need them every time. Let's use the static \Drupal::whatever() or, better, we can create some getters. See how \Drupal\editor\Editor::editorPluginManager() is used to lazy load the plugin.manager.editor service.
No, please! Are unrelated to this issue. Let's open another issue to handle those case.
I think, "... will be removed before Drupal 9.0.0." is more appropriate.
Overall:
Comment #15
mondrake#14:
thanks for review @claudiu.cristea.
Done, added #2629600: Remove deprecated function calls from ImageStyle
Added.
Done.
Not done.
Opened #2629598: Remove ImageStyle::createDerivative.
$image opens with the original image, but after applyAndSave it has all the properties aligned to the derivative that was saved. See also the new test.
Comment #16
mondrake...and the patch...
Comment #17
claudiu.cristeaOnly now I see this crap. So
ImageEffectInterface::applyEffect()does return a success flag but we are not using it here. We just return TRUE regardless if the effects were applied correct. Is there a good reason for this? I don't remember. If there's a reason, we need to document it, otherwise we need something like:So, we're not caching like in \Drupal\editor\Editor::editorPluginManager() in a protected variable? In the case the service is requested more than once per request, is there a cost to call again \Drupal::service()? I'm not sure, I think no and maybe there's no need to the protected cache var. Opinions?
In tests the container is available:
I'm not sure now that 'apply' verb is the best choice. We generally have the $image (the subject) and we 'apply' (the verb) something on it. See $image->apply('crop') — this makes perfectly sense. Here, the logic is reverted. "something" (image style) is the subject. Im not sure about the naming... Can we reflect more on this?
We have $this->assertNull().
This is just a recommendation: messages in assertions are obsolete.
Comment #18
mondrakeThanks @claudiu.cristea for #17.
1. The point is if we want to stop the derivative generation as soon as any effect fails, or (like now), just run all effects regardless of their failure, with each effect logging its own failure. A change here would be out of scope of this issue IMO. Let's have a separate issue for that, please.
2. Done like in Editor.
3. Done.
4. Any suggestion?
5. Done
6. OK, removed.
So to summarise remaining steps:
Comment #19
claudiu.cristeaI agree that this is off-topic. Also, I guess we still want to "show something" instead of nothing but also in this case we may want to
return $image->isValid();.OK, let's discuss this elsewhere.
Comment #20
mondrakeRe. #19, filed #2630224: ImageStyle::createDerivative should return fail if one of the style's effects fails.
(BTW I am fully in favour of #17.1 - IMHO we should not have a derivative at all if an effect fails - better that than a half baked derivative...)
Comment #21
claudiu.cristea->applyToImage() ?
Comment #22
claudiu.cristeaI think, better: ->applyTo() and ->applyToAndSave(). What you think?
Comment #23
fietserwinIMO, apply() is perfectly OK.
Most standard OO guidelines will tell us:
- method names are concise and clear.
- the combination of method name with object type and the name and type of arguments should be complete. Thus createDerivative() seems OK as create() alone is not enough.
- methods are preferably named using a single verb that tells what the object is doing (to or using the arguments of the method). Thus apply() is OK: the image style applies its effect to the (image) argument.
- method names do not repeat the type of the argument, thus not applyToImage() (or the shorter incomplete applyTo() that leaves out the type of the argument but still tries to connect the verb to the argument).
- method names that have 2 verbs indicate that the method does too much. Thus applyToAndSave() indicates that it would be better to split the method in a separate apply() and save() (I didn't look at the code if that is the case here).
Moreover, in this case we once decided that for consistency we would name all methods in the apply chain apply(). So better stick with that.
Comment #24
mondrakeDifferent problem now. We cannot add new methods to existing interfaces, not even in minor releases, it's a BC-break. See #2630242-6: Provide methods to retrieve EXIF image information via the Image object and following, and #1826362-46: ImageEffects of the same image style should be able to pass variables between them. Not sure how to deal with this now - a new interface just for the two methods?
EDIT - and if a new interface be, what name to give to it?
Comment #25
claudiu.cristea#24 is correct. I see two options:
ImageStyleActionsInterfaceand makeImageStyleimplements alsoImageStyleActionsInterface. Add next statements into docblock of this interface: "@deprecated in Drupal 8.1.x, will be merged into \Drupal\image\ImageStyleInterface in Drupal 9.0.x" and "@todo Remove this interface and merge its methods into \Drupal\image\ImageStyleInterface in 9.0.x".Which one? Maybe we should ask a core committer?
Comment #26
mondrakeUpdated issue summary, with what's open for decision/discussion.
Re. methods' names, I am also in favor of keeping apply() and applyAndSave() as @fietserwin suggests.
@fietserwin re. #23:
we already have an ImageStyle::save() method, which is used to save the ImageStyle configuration to storage - here the 'save' of the 'applyAndSave' is referring to the Image after 'application' of the style.
In fact I start to think that we should have a separate service where Image and ImageStyle are input to the building of the image derivative - rather than having all the logic into the ImageStyle config entity itself. But that would be something for D9 I believe.
Comment #27
mondrakeComment #28
fietserwin#24: technically correct, but I think this will result in nobody being able to:
- add new features unless we are talking completely self contained modules.
- refactor code, other then at the level of renaming internal variables and such.
- improve naming and staying BC by keeping the old method (explicit example in the docs about staying BC)
- extract services, out of existing code and referring to it.
Furthermore we are talking about:
- (IMO) a (to be) @internal API (https://www.drupal.org/node/2116841, https://www.drupal.org/node/2550249). I don't think that ImageStyleInterface is implemented in contrib by bypassing the ImageStyle core class (I even think it is not implemented at all in contrib)
- Not a disruptive change.
But let's indeed ask a core committer about the current consensus on this.
If the Interface has to remain locked for the whole D8 life cycle, I would go for the 1st option from @25 but not using @deprecated as there is no alternative so it doesn't make sense to have our IDEs warn us about something we can't change anyway.
[EDIT, crossposted]
re #26: If you can extract all this into a separate class, I would call that a new feature being allowed in a minor release.
Comment #29
mondrakeRerolled.
Comment #30
mondrakeback to NW per #25
Comment #37
mondrakeRelated, #2986669: Split ImageStyle into the config entity and a separate event-based image processing service