Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Jcrop Bundle Laravel Package

monstrum/symfony-jcrop-bundle

Symfony bundle that integrates the Jcrop jQuery plugin, adding the required assets and helpers so you can easily enable client-side image cropping in your Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

The bundle is a thin Symfony 2 wrapper around the jCrop jQuery plugin — it does not do image manipulation for you. Expect it to ship jCrop's JS/CSS as bundle assets plus (depending on the tag you pull) a Twig helper or a small config block for default jCrop options. Everything else — upload, persistence, actual cropping — is your code.

Minimal steps:

  1. Install. The Packagist name and the repo name differ (symfony-jcrop-bundle vs symfony-jcrop), so confirm the exact package before requiring:

    composer require monstrum/symfony-jcrop-bundle
    

    Check composer.json in the repo first — on a Symfony 2-era bundle the constraint is typically symfony/symfony: ~2.3 and PHP >=5.3.3. If you are on Symfony 3+, stop here; see Gotchas.

  2. Register the bundle in app/AppKernel.php:

    $bundles = array(
        // ...
        new Monstrum\JcropBundle\MonstrumJcropBundle(),
    );
    

    Confirm the class name by opening the bundle file in the repo — Monstrum's naming convention is MonstrumJcropBundle, but verify rather than guess.

  3. Publish assets so jCrop lands under web/bundles/:

    php app/console assets:install web --symlink
    
  4. Include jCrop plus jQuery in your layout — order matters (jQuery first, then jCrop, then your init script):

    <script src="{{ asset('bundles/monstrumjcrop/js/jquery.Jcrop.min.js') }}"></script>
    <link rel="stylesheet" href="{{ asset('bundles/monstrumjcrop/css/jquery.Jcrop.min.css') }}">
    

First use case: avatar upload with a crop step. Two routes — one that renders the just-uploaded image with a jCrop canvas, one that accepts the four coordinates (x, y, w, h) and writes the cropped file. If your first attempt is anything more ambitious than that, you are over-scoping a package this thin.

Where to look first: Resources/views/ for the shipped templates/macros and Resources/public/js/ for the actual jCrop version vendored in. That version number determines your jQuery compatibility more than anything the bundle documents.

Implementation Patterns

The two-request crop workflow

jCrop is purely client-side; the coordinates only exist in the browser until you POST them. The canonical pattern is a form with four hidden inputs populated by the onSelect/onChange callback:

{{ form_start(form) }}
  <img id="crop-target" src="{{ asset(image.webPath) }}" alt="">
  {{ form_widget(form.x) }}{{ form_widget(form.y) }}
  {{ form_widget(form.w) }}{{ form_widget(form.h) }}
  <button type="submit">Crop</button>
{{ form_end(form) }}

<script>
$(function () {
    $('#crop-target').Jcrop({
        aspectRatio: 1,
        setSelect: [0, 0, 200, 200],
        onSelect: function (c) {
            $('input[name$="[x]"]').val(Math.round(c.x));
            $('input[name$="[y]"]').val(Math.round(c.y));
            $('input[name$="[w]"]').val(Math.round(c.w));
            $('input[name$="[h]"]').val(Math.round(c.h));
        }
    });
});
</script>

Server-side cropping

The bundle will not resize for you. Pair it with Imagine (or raw GD) in a small service:

$img = $imagine->open($originalPath);
$img->crop(new Point($x, $y), new Box($w, $h))
    ->resize(new Box(200, 200))
    ->save($targetPath, array('quality' => 85));

Keep this in a dedicated service (app.cropper) rather than the controller — you will reuse it for every image type.

Coordinate scaling

The coordinates jCrop hands you are in displayed pixels, not original pixels. If you render a 4000px upload into a 600px container, you must scale:

$scale = $originalWidth / $displayWidth;
$x = (int) round($x * $scale);
// ...same for y, w, h

Store displayWidth as a hidden field at render time, or derive it from a fixed thumbnail width — but never assume displayed == original.

Form type integration

Wrap the four coordinates in a small CropType with HiddenType/hidden fields and NotBlank + Range constraints. This gets you CSRF, validation and handleRequest() for free, and keeps the controller down to five lines.

Config extension point

If the bundle exposes a monstrum_jcrop: config tree, use it for the default options (aspect ratio, min/max size) and read them back into the Twig helper. If it does not, don't fight it — keep your options in a Twig variable or a small JS file included per-page.

Gotchas and Tips

  • This is an unmaintained Symfony 2 artifact. Zero stars, zero dependents, and the description says "symfony2" explicitly. There is no Symfony 3/4/5/6 support, no Flex recipe, no autowiring, no services.yaml. Use it only to keep an existing legacy app running; for anything new, drop jCrop's files into web/ yourself or use a maintained alternative (Cropper.js, FilePond, or a modern symfony/ux component).
  • PHP version constraints will bite. Bundles of this vintage often declare ~5.3 or ~5.6 and pull in old Assetic. Running composer update on PHP 7.4+ frequently fails on transitive constraints. Prefer composer require --ignore-platform-reqs only as a last resort, and pin everything.
  • jQuery version clash. jCrop's later builds work with jQuery 1.9–2.x; jQuery 3 removed/changed enough internals that some jCrop releases throw on init. Keep a page-local jQuery 1.12 or 2.2 for the crop screen if the rest of your app is on 3.x.
  • Assetic vs assets:install. If the bundle's README shows {% stylesheets %} tags, that assumes Assetic is enabled. On a project where Assetic was removed, use asset('bundles/monstrumjcrop/...') instead and run assets:install after every composer update — otherwise you get silent 404s and an image that renders but never becomes selectable.
  • EXIF orientation. A phone photo with an orientation flag renders sideways and jCrop crops the wrong region. Run Imagick::setImageOrientation / autoRotate() before rendering the preview, so display and original agree.
  • Empty selection = zero-size crop. onSelect doesn't fire until the user drags. Default setSelect to something sensible, and validate w > 0 && h > 0 server-side or you will write 1x1 images. Also guard against coordinates exceeding the image bounds after scaling (rounding pushes them over by a pixel).
  • Don't trust the posted coordinates. They're client input. Clamp x + w <= width and y + h <= height against the server's knowledge of the image, or a crafted POST crops out of bounds and GD/Imagick throws or pads with black.
  • File cleanup. The uploaded original and the cropped derivative are two files. Delete-or-orphan bugs are the most common complaint in these crop workflows — decide ownership up front and clean up in a kernel.terminate listener or a scheduled command.
  • Debugging tip: if the crop handle overlay doesn't appear, it's almost always a missing CSS file, not JS. Check web/bundles/monstrumjcrop/css/ exists and returns 200 before touching the JS.
  • Extension points are basically templates. There is no plugin system here. To change markup, override Resources/views/ via {% extends %} or copy the template into app/Resources/MonstrumJcropBundle/views/. That override path is the only supported customization route — treat everything else as a fork.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource