PHPackages                             escolalms/images - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. escolalms/images

ActivePackage[Image &amp; Media](/categories/media)

escolalms/images
================

Escola Headless LMS Images manipulation

0.1.24(2y ago)113.3k↓50%1MITPHPPHP &gt;=7.4CI passing

Since Jul 30Pushed 1y ago2 watchersCompare

[ Source](https://github.com/EscolaLMS/Images)[ Packagist](https://packagist.org/packages/escolalms/images)[ RSS](/packages/escolalms-images/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (8)Versions (29)Used By (1)

Image processing module
=======================

[](#image-processing-module)

[![swagger](https://camo.githubusercontent.com/bf46f50926ef796b1bb0b6e41af746af52ff3aacdffb0533450f3b614a7334a2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f63756d656e746174696f6e2d737761676765722d677265656e)](https://escolalms.github.io/Images/)[![codecov](https://camo.githubusercontent.com/8d03cb84b9102cecc0d7bc7ca100a8fea171f820c7b2822f63f085c693519676/68747470733a2f2f636f6465636f762e696f2f67682f4573636f6c614c4d532f496d616765732f6272616e63682f6d61696e2f67726170682f62616467652e7376673f746f6b656e3d4e52414e34523841475a)](https://codecov.io/gh/EscolaLMS/Images)[![phpunit](https://github.com/EscolaLMS/Images/actions/workflows/test.yml/badge.svg)](https://github.com/EscolaLMS/Courses/actions/workflows/test.yml)[![downloads](https://camo.githubusercontent.com/150186a4b0e6fb4d77863f35243f27c72d32ffce5962cf7813a3b3d7d06180b9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6573636f6c616c6d732f696d61676573)](https://packagist.org/packages/escolalms/images)[![downloads](https://camo.githubusercontent.com/8f6ced9d8b1b924aac5b13e32e72d8ec38ed133e9bee60b8ae6432e5182ef942/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6573636f6c616c6d732f696d61676573)](https://packagist.org/packages/escolalms/images)[![downloads](https://camo.githubusercontent.com/d5e6ed07158e339bf0882e061da894a91d42ff3bd717fdb08c97c1cec960516c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6573636f6c616c6d732f696d61676573)](https://packagist.org/packages/escolalms/images)[![Maintainability](https://camo.githubusercontent.com/a0fc99d3a650cf36eefc0970987ebedf539fae5d02d06c509f18b5a6a7aeee04/68747470733a2f2f6170692e636f6465636c696d6174652e636f6d2f76312f6261646765732f37646665616530343632653335393937393762662f6d61696e7461696e6162696c697479)](https://codeclimate.com/github/EscolaLMS/Images/maintainability)[![Test Coverage](https://camo.githubusercontent.com/d1092340b4ba2a57a120f0325672a7a79bc956c5e9db58a99c39d9bcb8a78c6a/68747470733a2f2f6170692e636f6465636c696d6174652e636f6d2f76312f6261646765732f37646665616530343632653335393937393762662f746573745f636f766572616765)](https://codeclimate.com/github/EscolaLMS/Images/test_coverage)

What does it do
---------------

[](#what-does-it-do)

The package creates resized images from source by given parameters. This is a headless approach so the backend doesn't know the requested sizes before frontend requests any.

The input images are stored by Laravel in any of available disk (local storage/s3/any bucket). Once a resized version is requested a cached version in created and returend. Below are examples to show the overall idea.

The initial resize is done by [Intervention/image](https://github.com/Intervention/image) with `GD` driver. That can be [configured](http://image.intervention.io/getting_started/configuration).

After inital resized all the images are optimized with [image-optimizer](https://packagist.org/packages/spatie/image-optimizer).

For best results binaries must [be installed](https://github.com/spatie/image-optimizer#optimization-tools). EscolaLMS prepared [Docker Images](https://hub.docker.com/r/escolalms/php) are available for development (tag `work`) and production (tag `prod`).

Installation
------------

[](#installation)

- `composer require escolalms/images`
- `php artisan migrate`

Database
--------

[](#database)

- `image_caches` - table for saving the original and resized image path

idpathhash\_pathcreated\_atupdated\_at1avatars/1/avatar.jpgimgcache/68840270724ee4ff0b481f4fbd2299e13dfe2447.jpg2022-05-05 11:29:042022-05-05 11:29:043avatars/4/avatar4.jpgimgcache/d0f1ab43ae7f2924682b2ce734a24fa7066a8ea7.jpg2022-05-05 11:30:492022-05-05 11:30:492avatars/2/avatar2.jpgimgcache/94c6c83db6562161d38620a74e4e07fb3d9e39ed.jpg2022-05-05 11:30:392022-05-05 11:30:39Examples
--------

[](#examples)

### Default. One image as `302` redirect result .

[](#default-one-image-as-302-redirect-result-)

Basic resize is made by URL API call which redirects to new created file

Example `GET` call

- `http://localhost/api/images/img?path=test.jpg&w=100` call should return resized image to width 100px
- checks if file exsitis
- if not, creates one with availabe libraries
- returns 302 redirect
- example `http://localhost/storage/imgcache/891ee133a8bb111497d494d4c91fe292d9d16bb3.jpg` (assuming you're using local disk storage, in case of s3 location origin would differ)

### Resizing many images at once. JSON array as a result.

[](#resizing-many-images-at-once-json-array-as-a-result)

Example `POST` call like

```
POST /api/images/img HTTP/1.1
Host: localhost:1000
Content-Type: application/json
Content-Length: 212

{
  "paths": [{
    "path": "tutor_avatar.jpg",
    "params": {
      "w": 100
    }
  }, {
    "path": "tutor_avatar.jpg",
    "params": {
      "w": 200
    }
  }, {
    "path": "tutor_avatar.jpg",
    "params": {
      "w": 300
    }
  }]
}
```

generates following result

```
[
  {
    "url": "http://localhost/storage/imgcache/3421584c40d270d0fa7ef0c31445a1565db07cb4.jpg",
    "path": "imgcache/3421584c40d270d0fa7ef0c31445a1565db07cb4.jpg",
    "hash": "3421584c40d270d0fa7ef0c31445a1565db07cb4"
  },
  {
    "url": "http://localhost/storage/imgcache/7efc528c2cc7b57d79a42f80d2c1891b517cabfe.jpg",
    "path": "imgcache/7efc528c2cc7b57d79a42f80d2c1891b517cabfe.jpg",
    "hash": "7efc528c2cc7b57d79a42f80d2c1891b517cabfe"
  },
  {
    "url": "http://localhost/storage/imgcache/5db4f572d8c8b1cb6ad97a3bffc9fd6c18b56cc3.jpg",
    "path": "imgcache/5db4f572d8c8b1cb6ad97a3bffc9fd6c18b56cc3.jpg",
    "hash": "5db4f572d8c8b1cb6ad97a3bffc9fd6c18b56cc3"
  }
]
```

Hashing algorithm
-----------------

[](#hashing-algorithm)

There is simple algorithm to guess the result image URL. This allows frontend application to know the processed URL without calling API. As follows

```
$path = 'test.jpg';
$params = ['w'=>100];

$hash = sha1($path.json_encode($params));
```

then result URL would be

```
$output_file = $url_prefix.$hash.$extension;
```

Endpoint
--------

[](#endpoint)

There is API endpoints documentation [![swagger](https://camo.githubusercontent.com/bf46f50926ef796b1bb0b6e41af746af52ff3aacdffb0533450f3b614a7334a2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f63756d656e746174696f6e2d737761676765722d677265656e)](https://escolalms.github.io/Images/)

Tests
-----

[](#tests)

Run `./vendor/bin/phpunit` to run tests.

Test details [![codecov](https://camo.githubusercontent.com/8d03cb84b9102cecc0d7bc7ca100a8fea171f820c7b2822f63f085c693519676/68747470733a2f2f636f6465636f762e696f2f67682f4573636f6c614c4d532f496d616765732f6272616e63682f6d61696e2f67726170682f62616467652e7376673f746f6b656e3d4e52414e34523841475a)](https://codecov.io/gh/EscolaLMS/Images)[![phpunit](https://github.com/EscolaLMS/Images/actions/workflows/test.yml/badge.svg)](https://github.com/EscolaLMS/Courses/actions/workflows/test.yml)

Events
------

[](#events)

This package extends `filesystem`.

```
$this->app->extend('filesystem', function ($service, $app) {
    return new CustomFilesystemManager($app);
});

```

1. `FileDeleted` - The event is dispatched when you use `delete` method on the `Storage` facade.
2. `FileStored` - The event is dispatched when you use `put`, `putFile` or `putFileAs` method.

Listeners
---------

[](#listeners)

This package listens for `FileDeleted` and `FileStored` events and removes the resized images from the given path.

How to use this on frontend
---------------------------

[](#how-to-use-this-on-frontend)

Below is our totally **headless** approach on generating images

The following example tries to achives 2 purposes

- generate image on fly, frontend decide what sizes are needed
- images are not served by API

The idea is that since we know tha hashing algoritm for cached images we can guess that the URL will be like. If that URL is throwing 404 then we're calling the API endpoint to generate one. Fortunately this endpoint creates an requested image, caches it and returns redirect which is good for image src.

A major disadvantage of this approach is that first user once will get 404 in networking and experince few seconds delay before image is rendered after not founded.

```

  // Initial variables
  const imgPath = "tutor_avatar.jpg";
  const imgPrefix = "http://localhost/storage/imgcache";
  const apiUrl = "http://localhost/api/images/img";
  const rndWith = Math.round(Math.random() * 1000);
  const params = { w: rndWith.toString() }; // random width params
  // super important that all param values are strings
  // hash from { w: 100 } is different then { w: "100" }

  // stright forward helper to convert obejct to URL query params
  const paramsToUrl = (params) =>
    Object.entries(params)
      .map((e) => e.join("="))
      .join("&");

  /**
   * @param string path, example "tutor_avatar.jpg"
   * @param array params, example { w: "100" } or { w: "100", h: "10" }
   * @return Image
   */
  const getImage = (path, params) => {
    const hash = SHA1(path + JSON.stringify(params));
    const url = `${imgPrefix}/${hash}.${path.split(".").pop()}`;
    const imgApiUrl = `${apiUrl}/?path=${imgPath}&${paramsToUrl(params)}`;
    const image = new Image();
    image.src = url;
    image.onerror = () => {
      if (image.src != imgApiUrl) {
        // the cached version does not exists yet, lets call API to create one and redirect.
        image.src = imgApiUrl;
      }
    };

    return image;
  };

  document.body.appendChild(getImage(imgPath, params));

```

Working example is availabe in [doc](doc) folder.

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance32

Infrequent updates — may be unmaintained

Popularity26

Limited adoption so far

Community20

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~37 days

Recently: every ~162 days

Total

26

Last Release

824d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c857f3115c790c573d0617d7e9ba4011269ef7b9dfdb003dc4388846a62b14f7?d=identicon)[qunabu](/maintainers/qunabu)

---

Top Contributors

[![qunabu](https://avatars.githubusercontent.com/u/214608?v=4)](https://github.com/qunabu "qunabu (17 commits)")[![mako321](https://avatars.githubusercontent.com/u/59456825?v=4)](https://github.com/mako321 "mako321 (15 commits)")[![daVitekPL](https://avatars.githubusercontent.com/u/58150098?v=4)](https://github.com/daVitekPL "daVitekPL (9 commits)")[![pa-cholek](https://avatars.githubusercontent.com/u/5345420?v=4)](https://github.com/pa-cholek "pa-cholek (7 commits)")[![HerbertIV](https://avatars.githubusercontent.com/u/62691459?v=4)](https://github.com/HerbertIV "HerbertIV (3 commits)")[![dyfero](https://avatars.githubusercontent.com/u/59400506?v=4)](https://github.com/dyfero "dyfero (2 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/escolalms-images/health.svg)

```
[![Health](https://phpackages.com/badges/escolalms-images/health.svg)](https://phpackages.com/packages/escolalms-images)
```

###  Alternatives

[mostafaznv/nova-ckeditor

CkEditor for Laravel Nova

57327.2k1](/packages/mostafaznv-nova-ckeditor)[justbetter/statamic-image-optimize

Image optimization after upload

1315.2k](/packages/justbetter-statamic-image-optimize)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
