Skip to content

Commit

Permalink
Merge pull request #352 from plank/intervention-autoconfig
Browse files Browse the repository at this point in the history
attempt to automatically select an intervention/image driver
  • Loading branch information
frasmage committed Apr 9, 2024
2 parents e1222e3 + d0ecf82 commit 5b3628a
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 17 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog

## 6.1.0

* Attempt to automatically select an `intervention/image` driver based on the available extensions.
* Fix an error in package discovery if attempting to install both `plank/laravel-mediable` and `intervention/image-laravel` at the same time

## 6.0.5

* move ImageManipulator singleton to lazy instantiation

## 6.0.4

* Fix alt migration default value for the mysql dialect. Default value assigned from the Media model

## 6.0.3

* fix service provider database migration bindings by @frasmage in #349

## 6.0.2
- Added `intervention/image-laravel` package to the composer suggests list
- Updated documentation with configuration instructions for intervention/image
Expand Down
29 changes: 22 additions & 7 deletions docs/source/variants.rst
Expand Up @@ -10,30 +10,44 @@ Laravel-Mediable integrates the `intervention/image <http://image.intervention.i
Configure Intervention/image ImageManager
-----------------------------------------

Before you can use the ImageManipulation features of this package, you will need to make sure that the intervention/image package is properly configured. Intervention/image is capable of using either the `GD <https://www.php.net/manual/en/book.image.php>`_ or `ImageMagick <https://www.php.net/manual/en/book.imagick.php>`_ libraries as the underlying driver.
Before you can use the ImageManipulation features of this package, you will need to make sure that the intervention/image package is properly configured. Intervention/image is capable of using either the `GD <https://www.php.net/manual/en/book.image.php>`_ or `ImageMagick <https://www.php.net/manual/en/book.imagick.php>`_ PHP extensions as the underlying driver.

If intervention/image is not configured in the Laravel service container, this package will attempt to automatically select an intervention/image driver based on the extensions available, preferring `imagick` if available, and falling back to `gd`. If neither are available, attempting to use the ImageManipulator will result in an exception.

To configure Intervention/image yourself, refer to the following guides based on your version of the package.

Intervention/image >=3.0
^^^^^^^^^^^^^^^^^^^^^^^

RECOMMENDED: install the `intervention/image-laravel <https://image.intervention.io/v3/introduction/frameworks#laravel>`_ package to configure the container bindings automatically.

Alternatively, you can add the necessary bindings to the service container by adding the following to one of the service providers of your application.
Alternatively, you can add the necessary bindings to the service container manually by adding the following bindings to one of the service providers of your application.

::

<?php
// if using GD
$app->bind(Intervention\Image\Interfaces\DriverInterface::class, \Intervention\Image\Drivers\Gd\Driver::class);
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// if using GD
$app->bind(Intervention\Image\Interfaces\DriverInterface::class,
\Intervention\Image\Drivers\Gd\Driver::class
);

// if using Imagick
$app->bind(Intervention\Image\Interfaces\DriverInterface::class, \Intervention\Image\Drivers\Imagick\Driver::class);
// if using Imagick
$app->bind(Intervention\Image\Interfaces\DriverInterface::class,
\Intervention\Image\Drivers\Imagick\Driver::class
);
}
}

Intervention/image <3.0
^^^^^^^^^^^^^^^^^^^^^^^

RECOMMENDED: `follow the steps <https://image.intervention.io/v2/introduction/installation#integration-in-laravel>`_ to enable the intervention/image Laravel service provider.

Otherwise, by default, intervention/image will use the `GD <https://www.php.net/manual/en/book.image.php>`_ library driver. If you intend to use the additional features of the `ImageMagick <https://www.php.net/manual/en/book.imagick.php>`_ driver, you should make sure that the PHP extension is installed and the correct configuration is bound to the Laravel service container.
Alternatively, you can add the necessary bindings to the service container manually by adding the following bindings to one of the service providers of your application.

::

Expand All @@ -49,6 +63,7 @@ Otherwise, by default, intervention/image will use the `GD <https://www.php.net/
ImageManager::class,
function() {
return new ImageManager(['driver' => 'imagick']);
// return new ImageManager(['driver' => 'gd']);
}
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/Exceptions/MediaUpload/ConfigurationException.php
Expand Up @@ -52,4 +52,9 @@ public static function invalidOptimizer(string $optimizerClass): self
{
return new self("Invalid optimizer class `{$optimizerClass}`. Must implement `\Spatie\ImageOptimizer\Optimizer`.");
}

public static function interventionImageNotConfigured(): self
{
return new self("Before variants can be created, the intervention/image package must be configured in the Laravel container.");
}
}
16 changes: 14 additions & 2 deletions src/ImageManipulator.php
Expand Up @@ -9,14 +9,15 @@
use Intervention\Image\Image;
use Intervention\Image\ImageManager;
use Plank\Mediable\Exceptions\ImageManipulationException;
use Plank\Mediable\Exceptions\MediaUpload\ConfigurationException;
use Plank\Mediable\SourceAdapters\SourceAdapterInterface;
use Plank\Mediable\SourceAdapters\StreamAdapter;
use Psr\Http\Message\StreamInterface;
use Spatie\ImageOptimizer\OptimizerChain;

class ImageManipulator
{
private ImageManager $imageManager;
private ?ImageManager $imageManager;

/**
* @var ImageManipulation[]
Expand All @@ -33,7 +34,7 @@ class ImageManipulator
private ImageOptimizer $imageOptimizer;

public function __construct(
ImageManager $imageManager,
?ImageManager $imageManager,
FilesystemManager $filesystem,
ImageOptimizer $imageOptimizer
) {
Expand All @@ -47,6 +48,9 @@ public function defineVariant(
ImageManipulation $manipulation,
?array $tags = []
) {
if (!$this->imageManager) {
throw ConfigurationException::interventionImageNotConfigured();
}
$this->variantDefinitions[$variantName] = $manipulation;
foreach ($tags as $tag) {
$this->variantDefinitionGroups[$tag][] = $variantName;
Expand Down Expand Up @@ -105,6 +109,10 @@ public function createImageVariant(
string $variantName,
bool $forceRecreate = false
): Media {
if (!$this->imageManager) {
throw ConfigurationException::interventionImageNotConfigured();
}

$this->validateMedia($media);

$modelClass = config('mediable.model');
Expand Down Expand Up @@ -217,6 +225,10 @@ public function manipulateUpload(
SourceAdapterInterface $source,
ImageManipulation $manipulation
): StreamAdapter {
if (!$this->imageManager) {
throw ConfigurationException::interventionImageNotConfigured();
}

$outputFormat = $this->determineOutputFormat($manipulation, $media);
if (method_exists($this->imageManager, 'read')) {
// Intervention Image >=3.0
Expand Down
46 changes: 41 additions & 5 deletions src/MediableServiceProvider.php
Expand Up @@ -3,13 +3,11 @@

namespace Plank\Mediable;

use CreateMediableTables;
use Illuminate\Contracts\Container\Container;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Filesystem\FilesystemManager;
use Illuminate\Support\ServiceProvider;
use Intervention\Image\ImageManager;
use Mimey\MimeTypes;
use Intervention\Image\Interfaces\DriverInterface;
use Plank\Mediable\Commands\ImportMediaCommand;
use Plank\Mediable\Commands\PruneMediaCommand;
use Plank\Mediable\Commands\SyncMediaCommand;
Expand Down Expand Up @@ -108,11 +106,11 @@ public function register(): void
);

$this->registerSourceAdapterFactory();
$this->registerImageManipulator();
$this->registerUploader();
$this->registerMover();
$this->registerUrlGeneratorFactory();
$this->registerConsoleCommands();
$this->registerImageManipulator();
}

/**
Expand Down Expand Up @@ -193,7 +191,7 @@ public function registerImageManipulator(): void
{
$this->app->singleton(ImageManipulator::class, function (Container $app) {
return new ImageManipulator(
$app->get(ImageManager::class),
$this->getInterventionImageManagerConfiguration($app),
$app->get(FilesystemManager::class),
$app->get(ImageOptimizer::class)
);
Expand All @@ -212,4 +210,42 @@ public function registerConsoleCommands(): void
SyncMediaCommand::class,
]);
}

private function getInterventionImageManagerConfiguration(Container $app): ?ImageManager
{
$imageManager = null;
if ($app->has(ImageManager::class)
|| (
class_exists(DriverInterface::class) // intervention >= 3.0
&& $app->has(DriverInterface::class)
)
) {
// use whatever the user has bound to the container if available
$imageManager = $app->get(ImageManager::class);
} elseif (extension_loaded('imagick')) {
// attempt to automatically configure for imagick
if (class_exists(\Intervention\Image\Drivers\Imagick\Driver::class)) {
// intervention/image >=3.0
$imageManager = new ImageManager(
new \Intervention\Image\Drivers\Imagick\Driver()
);
} else {
// intervention/image <3.0
$imageManager = new ImageManager(['driver' => 'imagick']);
}
} elseif (extension_loaded('gd')) {
// attempt to automatically configure for gd
if (class_exists(\Intervention\Image\Drivers\GD\Driver::class)) {
// intervention/image >=3.0
$imageManager = new ImageManager(
new \Intervention\Image\Drivers\GD\Driver()
);
} else {
// intervention/image <3.0
$imageManager = new ImageManager(['driver' => 'gd']);
}
}

return $imageManager;
}
}
48 changes: 48 additions & 0 deletions tests/Integration/ImageManipulatorTest.php
Expand Up @@ -7,10 +7,12 @@
use Intervention\Image\Image;
use Intervention\Image\ImageManager;
use Plank\Mediable\Exceptions\ImageManipulationException;
use Plank\Mediable\Exceptions\MediaUpload\ConfigurationException;
use Plank\Mediable\ImageManipulation;
use Plank\Mediable\ImageManipulator;
use Plank\Mediable\ImageOptimizer;
use Plank\Mediable\Media;
use Plank\Mediable\SourceAdapters\SourceAdapterInterface;
use Plank\Mediable\Tests\TestCase;
use Spatie\ImageOptimizer\Optimizers\Optipng;
use Spatie\ImageOptimizer\Optimizers\Pngquant;
Expand Down Expand Up @@ -765,4 +767,50 @@ public function getManipulator(): ImageManipulator
);
}
}

public function test_it_throws_if_no_intervention_image_define_variant(): void
{
$this->expectException(ConfigurationException::class);
$manipulator = new ImageManipulator(
null,
app(FilesystemManager::class),
app(ImageOptimizer::class)
);

$manipulator->defineVariant(
'foo',
new ImageManipulation($this->getMockCallable())
);
}

public function test_it_throws_if_no_intervention_image_create_image_variant(): void
{
$this->expectException(ConfigurationException::class);
$manipulator = new ImageManipulator(
null,
app(FilesystemManager::class),
app(ImageOptimizer::class)
);

$manipulator->createImageVariant(
new Media,
'foo'
);
}

public function test_it_throws_if_no_intervention_image_manipulate_upload(): void
{
$this->expectException(ConfigurationException::class);
$manipulator = new ImageManipulator(
null,
app(FilesystemManager::class),
app(ImageOptimizer::class)
);

$manipulator->manipulateUpload(
new Media,
$this->createMock(SourceAdapterInterface::class),
$this->createMock(ImageManipulation::class),
);
}
}
7 changes: 4 additions & 3 deletions tests/Integration/MediaUploaderTest.php
Expand Up @@ -823,7 +823,8 @@ public function test_it_manipulates_images(): void
function (Image $image) {
$image->resize(16, 16);
}
)->outputJpegFormat();
)->outputJpegFormat()
->noOptimization();

app(ImageManipulator::class)->defineVariant(
'foo',
Expand All @@ -843,8 +844,8 @@ function (Image $image) {
$this->assertEquals('image/jpeg', $media->mime_type);
$this->assertEquals('image', $media->aggregate_type);
$this->assertTrue(
$media->size >= 933 // intervention/image <3.0
&& $media->size <= 951 // intervention/image >=3.0
$media->size <= 951, // intervention/image >=3.0,
"got size {$media->size}"
);
}

Expand Down

0 comments on commit 5b3628a

Please sign in to comment.