From ebf3a8fc4179c537fbf185dc79b9a16d06e4e20c Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Sun, 22 Apr 2018 18:03:41 -0400 Subject: [PATCH 1/2] Added CraftQL support Signed-off-by: Andrew Welch --- src/ImageOptimize.php | 216 +++++++++++++++++------------ src/listeners/GetCraftQLSchema.php | 70 ++++++++++ src/models/OptimizedImage.php | 117 ++++++++++++++-- 3 files changed, 298 insertions(+), 105 deletions(-) create mode 100644 src/listeners/GetCraftQLSchema.php diff --git a/src/ImageOptimize.php b/src/ImageOptimize.php index 43b324aa..c1be3356 100644 --- a/src/ImageOptimize.php +++ b/src/ImageOptimize.php @@ -12,6 +12,7 @@ use nystudio107\imageoptimize\fields\OptimizedImages; use nystudio107\imageoptimize\imagetransforms\ImageTransformInterface; +use nystudio107\imageoptimize\listeners\GetCraftQLSchema; use nystudio107\imageoptimize\models\Settings; use nystudio107\imageoptimize\services\Optimize as OptimizeService; use nystudio107\imageoptimize\services\OptimizedImages as OptimizedImagesService; @@ -22,7 +23,6 @@ use craft\base\Field; use craft\base\Plugin; use craft\base\Volume; -use craft\console\Application as ConsoleApplication; use craft\elements\Asset; use craft\events\AssetTransformImageEvent; use craft\events\ElementEvent; @@ -44,7 +44,10 @@ use craft\web\twig\variables\CraftVariable; use craft\web\Controller; +use markhuot\CraftQL\CraftQL; + use yii\base\Event; +use yii\base\Exception; /** @noinspection MissingPropertyAnnotationsInspection */ @@ -63,6 +66,12 @@ */ class ImageOptimize extends Plugin { + + // Constants + // ========================================================================= + + const CRAFTQL_PLUGIN_HANDLE = 'craftql'; + // Static Properties // ========================================================================= @@ -150,15 +159,23 @@ public function settingsHtml() $settings = $this->getSettings(); // Render the settings template - return Craft::$app->getView()->renderTemplate( - 'image-optimize/settings', - [ - 'settings' => $settings, - 'imageProcessors' => $imageProcessors, - 'variantCreators' => $variantCreators, - 'gdInstalled' => function_exists('imagecreatefromjpeg'), - ] - ); + try { + return Craft::$app->getView()->renderTemplate( + 'image-optimize/settings', + [ + 'settings' => $settings, + 'imageProcessors' => $imageProcessors, + 'variantCreators' => $variantCreators, + 'gdInstalled' => \function_exists('imagecreatefromjpeg'), + ] + ); + } catch (\Twig_Error_Loader $e) { + Craft::error($e->getMessage(), __METHOD__); + } catch (Exception $e) { + Craft::error($e->getMessage(), __METHOD__); + } + + return ''; } // Protected Methods @@ -210,83 +227,7 @@ protected function installEventHandlers() $this->installAssetEventHandlers(); $this->installElementEventHandlers(); $this->installMiscEventHandlers(); - } - - /** - * Install our miscellaneous event handlers - */ - protected function installMiscEventHandlers() - { - // Handler: Fields::EVENT_AFTER_SAVE_FIELD - Event::on( - Fields::class, - Fields::EVENT_AFTER_SAVE_FIELD, - function (FieldEvent $event) { - Craft::debug( - 'Fields::EVENT_AFTER_SAVE_FIELD', - __METHOD__ - ); - $settings = $this->getSettings(); - /** @var Field $field */ - if (!$event->isNew && $settings->automaticallyResaveImageVariants) { - $this->checkForOptimizedImagesField($event); - } - } - ); - - // Handler: Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS - Event::on( - Plugins::class, - Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS, - function (PluginEvent $event) { - if ($event->plugin === $this) { - Craft::debug( - 'Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS', - __METHOD__ - ); - $settings = $this->getSettings(); - if ($settings->automaticallyResaveImageVariants) { - // After they have changed the settings, resave all of the assets - ImageOptimize::$plugin->optimizedImages->resaveAllVolumesAssets(); - } - } - } - ); - - // Handler: Volumes::EVENT_AFTER_SAVE_VOLUME - Event::on( - Volumes::class, - Volumes::EVENT_AFTER_SAVE_VOLUME, - function (VolumeEvent $event) { - Craft::debug( - 'Volumes::EVENT_AFTER_SAVE_VOLUME', - __METHOD__ - ); - $settings = $this->getSettings(); - // Only worry about this volume if it's not new - if (!$event->isNew && $settings->automaticallyResaveImageVariants) { - /** @var Volume $volume */ - $volume = $event->volume; - if (!empty($volume)) { - ImageOptimize::$plugin->optimizedImages->resaveVolumeAssets($volume); - } - } - } - ); - - // Handler: Plugins::EVENT_AFTER_INSTALL_PLUGIN - Event::on( - Plugins::class, - Plugins::EVENT_AFTER_INSTALL_PLUGIN, - function (PluginEvent $event) { - if ($event->plugin === $this) { - $request = Craft::$app->getRequest(); - if ($request->isCpRequest) { - Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('image-optimize/welcome'))->send(); - } - } - } - ); + $this->installCraftQLEventHandlers(); } /** @@ -375,7 +316,7 @@ function (ReplaceAssetEvent $event) { ); /** @var Asset $element */ $element = $event->asset; - if (!empty($element->id)) { + if ($element->id !== null) { ImageOptimize::$plugin->optimizedImages->resaveAsset($element->id); } } @@ -434,9 +375,101 @@ function (ElementEvent $event) { ); } + + /** + * Install our miscellaneous event handlers + */ + protected function installMiscEventHandlers() + { + // Handler: Fields::EVENT_AFTER_SAVE_FIELD + Event::on( + Fields::class, + Fields::EVENT_AFTER_SAVE_FIELD, + function (FieldEvent $event) { + Craft::debug( + 'Fields::EVENT_AFTER_SAVE_FIELD', + __METHOD__ + ); + $settings = $this->getSettings(); + /** @var Field $field */ + if (!$event->isNew && $settings->automaticallyResaveImageVariants) { + $this->checkForOptimizedImagesField($event); + } + } + ); + + // Handler: Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS + Event::on( + Plugins::class, + Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS, + function (PluginEvent $event) { + if ($event->plugin === $this) { + Craft::debug( + 'Plugins::EVENT_AFTER_SAVE_PLUGIN_SETTINGS', + __METHOD__ + ); + $settings = $this->getSettings(); + if ($settings->automaticallyResaveImageVariants) { + // After they have changed the settings, resave all of the assets + ImageOptimize::$plugin->optimizedImages->resaveAllVolumesAssets(); + } + } + } + ); + + // Handler: Volumes::EVENT_AFTER_SAVE_VOLUME + Event::on( + Volumes::class, + Volumes::EVENT_AFTER_SAVE_VOLUME, + function (VolumeEvent $event) { + Craft::debug( + 'Volumes::EVENT_AFTER_SAVE_VOLUME', + __METHOD__ + ); + $settings = $this->getSettings(); + // Only worry about this volume if it's not new + if (!$event->isNew && $settings->automaticallyResaveImageVariants) { + /** @var Volume $volume */ + $volume = $event->volume; + if ($volume !== null) { + ImageOptimize::$plugin->optimizedImages->resaveVolumeAssets($volume); + } + } + } + ); + + // Handler: Plugins::EVENT_AFTER_INSTALL_PLUGIN + Event::on( + Plugins::class, + Plugins::EVENT_AFTER_INSTALL_PLUGIN, + function (PluginEvent $event) { + if ($event->plugin === $this) { + $request = Craft::$app->getRequest(); + if ($request->isCpRequest) { + Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('image-optimize/welcome'))->send(); + } + } + } + ); + } + + /** + * Install our CraftQL event handlers + */ + protected function installCraftQLEventHandlers() + { + if (class_exists(CraftQL::class)) { + Event::on( + OptimizedImages::class, + GetCraftQLSchema::EVENT_GET_FIELD_SCHEMA, + [new GetCraftQLSchema, 'handle'] + ); + } + } + /** - * If the Field being saved is an OptimizedImages field, re-save the responsive - * image variants automatically + * If the Field being saved is an OptimizedImages field, re-save the + * responsive image variants automatically * * @param FieldEvent $event * @@ -456,7 +489,8 @@ protected function checkForOptimizedImagesField(FieldEvent $event) if ($fieldLayout) { $fields = $fieldLayout->getFields(); foreach ($fields as $field) { - if ($thisField->handle == $field->handle) { + /** @var Field $field */ + if ($thisField->handle === $field->handle) { $needToReSave = true; } } diff --git a/src/listeners/GetCraftQLSchema.php b/src/listeners/GetCraftQLSchema.php new file mode 100644 index 00000000..cb0de108 --- /dev/null +++ b/src/listeners/GetCraftQLSchema.php @@ -0,0 +1,70 @@ +handled = true; + $field = $event->sender; + + $fieldObject = $event->schema->createObjectType('OptimizedImagesData'); + + // Primary getter functions + $fieldObject->addStringField('src'); + $fieldObject->addStringField('srcset'); + $fieldObject->addStringField('srcWebp'); + $fieldObject->addStringField('srcsetWebp'); + $fieldObject->addIntField('maxSrcsetWidth'); + $fieldObject->addStringField('placeholderImage'); + $fieldObject->addStringField('placeholderBox'); + $fieldObject->addStringField('placeholderSilhouette'); + + // Object properties + $fieldObject->addStringField('optimizedImageUrls') + ->lists() + ->type(Type::string()); + $fieldObject->addStringField('optimizedWebPImageUrls') + ->lists() + ->type(Type::string()); + $fieldObject->addIntField('variantSourceWidths') + ->lists() + ->type(Type::int()); + $fieldObject->addIntField('originalImageWidth'); + $fieldObject->addIntField('originalImageHeight'); + $fieldObject->addStringField('placeholder'); + $fieldObject->addStringField('placeholderSvg'); + $fieldObject->addStringField('colorPalette') + ->lists() + ->type(Type::string()); + $fieldObject->addIntField('placeholderWidth'); + $fieldObject->addIntField('placeholderHeight'); + + // Add the field object to the schema + $event->schema->addField($event->sender) + ->type($fieldObject); + } +} diff --git a/src/models/OptimizedImage.php b/src/models/OptimizedImage.php index dbdd75e1..61c0e9ae 100644 --- a/src/models/OptimizedImage.php +++ b/src/models/OptimizedImage.php @@ -88,7 +88,7 @@ class OptimizedImage extends Model /** * @inheritdoc */ - public function rules() + public function rules(): array { return [ ['optimizedImageUrls', ArrayValidator::class], @@ -117,9 +117,21 @@ public function src(int $width = 0): string { if (empty($width)) { return Template::raw(reset($this->optimizedImageUrls)); - } else { - return Template::raw($this->optimizedImageUrls[$width] ?? ''); } + + return Template::raw($this->optimizedImageUrls[$width] ?? ''); + } + + /** + * Getter for CraftQL + * + * @param int $width + * + * @return null|string|\Twig_Markup + */ + public function getSrc(int $width = 0): string + { + return $this->src($width); } /** @@ -135,6 +147,17 @@ public function srcset(bool $dpr = false): string return Template::raw($this->getSrcsetFromArray($this->optimizedImageUrls, $dpr)); } + /** + * Getter for CraftQL + * + * @param bool $dpr + * + * @return string + */ + public function getSrcset(bool $dpr = false): string + { + return $this->srcset($dpr); + } /** * Return a string of image URLs and their sizes that match $width * @@ -196,9 +219,21 @@ public function srcWebp(int $width = 0): string { if (empty($width)) { return Template::raw(reset($this->optimizedWebPImageUrls)); - } else { - return Template::raw($this->optimizedWebPImageUrls[$width] ?? ''); } + + return Template::raw($this->optimizedWebPImageUrls[$width] ?? ''); + } + + /** + * Getter for CraftQL + * + * @param int $width + * + * @return string + */ + public function getSrcWebp(int $width = 0): string + { + return $this->srcWebp($width); } /** @@ -214,6 +249,18 @@ public function srcsetWebp(bool $dpr = false): string return Template::raw($this->getSrcsetFromArray($this->optimizedWebPImageUrls, $dpr)); } + /** + * Getter for CraftQL + * + * @param bool $dpr + * + * @return string + */ + public function getSrcsetWebp(bool $dpr = false): string + { + return $this->srcsetWebp($dpr); + } + /** * Return a string of webp image URLs and their sizes that match $width * @@ -285,6 +332,16 @@ public function maxSrcsetWidth(): int return $result; } + /** + * Getter for CraftQL + * + * @return int + */ + public function getMaxSrcsetWidth(): int + { + return $this->maxSrcsetWidth(); + } + /** * Return a base64-encoded placeholder image * @@ -303,13 +360,23 @@ public function placeholderImage() return Template::raw($header.rawurlencode($content)); } + /** + * Getter for CraftQL + * + * @return string + */ + public function getPlaceholderImage(): string + { + return (string)$this->placeholderImage(); + } + /** * @return string */ - public function placeholderImageSize() + public function placeholderImageSize(): string { $placeholder = $this->placeholderImage(); - $contentLength = !empty(strlen($placeholder)) ? strlen($placeholder) : 0; + $contentLength = !empty(\strlen($placeholder)) ? \strlen($placeholder) : 0; return ImageOptimize::$plugin->optimize->humanFileSize($contentLength, 1); } @@ -321,7 +388,7 @@ public function placeholderImageSize() * * @return \Twig_Markup|null */ - public function placeholderBox($color = null) + public function placeholderBox(string $color = null) { $width = $this->placeholderWidth ?? 1; $height = $this->placeholderHeight ?? 1; @@ -331,12 +398,24 @@ public function placeholderBox($color = null) } /** + * @param string|null $color + * + * @return string + */ + public function getPlaceholderBox(string $color = null): string + { + return (string)$this->placeholderBox($color); + } + + /** + * Getter for CraftQL + * * @return string */ - public function placeholderBoxSize() + public function placeholderBoxSize(): string { $placeholder = $this->placeholderBox(); - $contentLength = !empty(strlen($placeholder)) ? strlen($placeholder) : 0; + $contentLength = !empty(\strlen($placeholder)) ? \strlen($placeholder) : 0; return ImageOptimize::$plugin->optimize->humanFileSize($contentLength, 1); } @@ -359,13 +438,23 @@ public function placeholderSilhouette() return Template::raw($header.$content); } + /** + * Getter for CraftQL + * + * @return string + */ + public function getPlaceholderSilhouette(): string + { + return (string)$this->placeholderSilhouette(); + } + /** * @return string */ - public function placeholderSilhouetteSize() + public function placeholderSilhouetteSize(): string { $placeholder = $this->placeholderSilhouette(); - $contentLength = !empty(strlen($placeholder)) ? strlen($placeholder) : 0; + $contentLength = !empty(\strlen($placeholder)) ? \strlen($placeholder) : 0; return ImageOptimize::$plugin->optimize->humanFileSize($contentLength, 1); } @@ -468,10 +557,10 @@ protected function getSrcsetFromArray(array $array, bool $dpr = false): string foreach ($array as $key => $value) { if ($dpr) { $descriptor = '1x'; - if (!empty($array[intval($key) / 2])) { + if (!empty($array[(int)$key / 2])) { $descriptor = '2x'; } - if (!empty($array[intval($key) / 3])) { + if (!empty($array[(int)$key / 3])) { $descriptor = '3x'; } } else { From 83abb8de8a911d42fb7e57d56b28cf3fcca04c6a Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Sun, 22 Apr 2018 18:04:13 -0400 Subject: [PATCH 2/2] Version 1.4.31 Signed-off-by: Andrew Welch --- CHANGELOG.md | 4 ++++ README.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ composer.json | 2 +- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11df2973..17df8672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # ImageOptimize Changelog +## 1.4.31 - 2018.04.22 +### Added +* Added CraftQL support + ## 1.4.30 - 2018.04.09 ### Added * Added additional profiling information diff --git a/README.md b/README.md index 05836984..1ec4efb4 100644 --- a/README.md +++ b/README.md @@ -533,6 +533,71 @@ Re-saving many images at a time can be intensive, and on certain setups may requ All you need to do is install the plugin, and any queue jobs in Craft CMS 3 will now run entirely in the background via the CLI php, which isn't subject to the same restrictions that the web php is. +### GraphQL via CraftQL Plugin + +ImageOptimize has built-in support for accessing the OptimizedImages field via GraphQL using the [CraftQL plugin](https://github.com/markhuot/craftql). + +You can access all of the primary OptimizeImages methods: + +``` +{ + entries(section:[homepage], limit:1) { + ...on Homepage { + title + url + someAsset { + ...on AssetsVolume { + title + optimizedImages { + ...on OptimizedImagesData { + src, + srcset, + srcWebp, + srcsetWebp, + maxSrcsetWidth, + placeholderImage, + placeholderBox, + placeholderSilhouette + } + } + } + } + } + } +} +``` + +...as well as all of the object properties: + +``` + entries(section:[homepage], limit:1) { + ...on Homepage { + title + url + someAsset { + ...on AssetsVolume { + title + optimizedImages { + ...on OptimizedImagesData { + optimizedImageUrls, + optimizedWebPImageUrls, + variantSourceWidths, + originalImageWidth, + originalImageHeight + placeholder, + placeholderSvg, + colorPalette, + placeholderWidth, + placeholderHeight + } + } + } + } + } + } +} +``` + ## Using Optimized Image Transforms Once ImageOptimize is set up and configured, there's nothing left to do for optimizing your image transforms. It just works. diff --git a/composer.json b/composer.json index 711882fe..9342051a 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "nystudio107/craft-imageoptimize", "description": "Automatically create & optimize responsive image transforms, using either native Craft transforms or a service like Imgix, with zero template changes.", "type": "craft-plugin", - "version": "1.4.30", + "version": "1.4.31", "keywords": [ "craft", "cms",