Resizing Images
On the conference page design, photos are constrained to a maximum size of 200 by 150 pixels. What about optimizing the images and reducing their size if the uploaded original is larger than the limits?
That is a perfect job that can be added to the comment workflow, probably just after the comment is validated and just before it is published.
Let's add a new ready state and an optimize transition:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
--- i/config/packages/workflow.yaml
+++ w/config/packages/workflow.yaml
@@ -16,6 +16,7 @@ framework:
- potential_spam
- spam
- rejected
+ - ready
- published
transitions:
accept:
@@ -29,13 +30,16 @@ framework:
to: spam
publish:
from: potential_spam
- to: published
+ to: ready
reject:
from: potential_spam
to: rejected
publish_ham:
from: ham
- to: published
+ to: ready
reject_ham:
from: ham
to: rejected
+ optimize:
+ from: ready
+ to: published
Generate a visual representation of the new workflow configuration to validate that it describes what we want:
1
$ symfony console workflow:dump comment | dot -Tpng -o workflow.png
Optimizing Images with Imagine
Image optimizations will be done thanks to GD (check that your local PHP installation has the GD extension enabled) and Imagine:
1
$ symfony composer req "imagine/imagine:^1.2"
Resizing an image can be done via the following service class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
namespace App;
use Imagine\Gd\Imagine;
use Imagine\Image\Box;
class ImageOptimizer
{
private const MAX_WIDTH = 200;
private const MAX_HEIGHT = 150;
private $imagine;
public function __construct()
{
$this->imagine = new Imagine();
}
public function resize(string $filename): void
{
list($iwidth, $iheight) = getimagesize($filename);
$ratio = $iwidth / $iheight;
$width = self::MAX_WIDTH;
$height = self::MAX_HEIGHT;
if ($width / $height > $ratio) {
$width = $height * $ratio;
} else {
$height = $width / $ratio;
}
$photo = $this->imagine->open($filename);
$photo->resize(new Box($width, $height))->save($filename);
}
}
After optimizing the photo, we store the new file in place of the original one. You might want to keep the original image around though.
Adding a new Step in the Workflow
Modify the workflow to handle the new state:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
--- i/src/MessageHandler/CommentMessageHandler.php
+++ w/src/MessageHandler/CommentMessageHandler.php
@@ -2,6 +2,7 @@
namespace App\MessageHandler;
+use App\ImageOptimizer;
use App\Message\CommentMessage;
use App\Repository\CommentRepository;
use App\SpamChecker;
@@ -25,6 +26,8 @@ class CommentMessageHandler
private WorkflowInterface $commentStateMachine,
private MailerInterface $mailer,
#[Autowire('%admin_email%')] private string $adminEmail,
+ private ImageOptimizer $imageOptimizer,
+ #[Autowire('%photo_dir%')] private string $photoDir,
private ?LoggerInterface $logger = null,
) {
}
@@ -54,6 +57,12 @@ class CommentMessageHandler
->to($this->adminEmail)
->context(['comment' => $comment])
);
+ } elseif ($this->commentStateMachine->can($comment, 'optimize')) {
+ if ($comment->getPhotoFilename()) {
+ $this->imageOptimizer->resize($this->photoDir.'/'.$comment->getPhotoFilename());
+ }
+ $this->commentStateMachine->apply($comment, 'optimize');
+ $this->entityManager->flush();
} elseif ($this->logger) {
$this->logger->debug('Dropping comment message', ['comment' => $comment->getId(), 'state' => $comment->getState()]);
}
Note that $photoDir is automatically injected as we defined a container parameter on this variable name in a previous step:
1 2
parameters:
photo_dir: "%kernel.project_dir%/public/uploads/photos"
Storing Uploaded Data in Production
We have already defined a special read-write directory for uploaded files in .platform.app.yaml. But the mount is local. If we want the web container and the message consumer worker to be able to access the same mount, we need to create a file service:
1 2 3 4 5 6 7 8 9 10
--- i/.platform/services.yaml
+++ w/.platform/services.yaml
@@ -12,3 +12,7 @@ varnish:
vcl: !include
type: string
path: config.vcl
+
+files:
+ type: network-storage:2.0
+ disk: 256
Use it for the photos upload directory:
1 2 3 4 5 6 7 8 9 10 11
--- i/.platform.app.yaml
+++ w/.platform.app.yaml
@@ -31,7 +31,7 @@ web:
mounts:
"/var/cache": { source: local, source_path: var/cache }
- "/public/uploads": { source: local, source_path: uploads }
+ "/public/uploads": { source: service, service: files, source_path: uploads }
relationships:
This should be enough to make the feature work in production.