Code Monkey home page Code Monkey logo

reorder-before-after's Introduction

reorder-before-after

Reorder an item in an array before or after another.

Summary

About

I make a web app in which my users can reorder before or after another.

I did not find any package to do what I search for, and since I use it at multiple place on my app I figured I would open source it in the hope it can help simplify the life of another developer out there!

Features

  • Can reorder an item before or after another in a list of items
  • Systematically "self-heals" the items orders by re-writing the index of all items starting from the minimum index

Requirements

  • PHP >= 8.1
  • Composer

Installation

In your terminal, on the root of your project folder, run:

composer require khalyomede/reorder-before-after

Examples

1. Moving an item before another

In this example, we will move our book before our table.

use Khalyomede\ReorderBeforeAfter\Item;
use Khalyomede\ReorderBeforeAfter\Listing;
use Khalyomede\ReorderBeforeAfter\Placement;

$listing = new Listing();

$listing->push(new Item("bag", 1));
$listing->push(new Item("chair", 2));
$listing->push(new Item("table", 3));
$listing->push(new Item("book", 4));

$listing->reorder("book", Placement::Before, "table");

assert($listing->find("bag")->order === 1);
assert($listing->find("chair")->order === 2);
assert($listing->find("book")->order === 3);
assert($listing->find("table")->order === 4);

2. Create a list of items out of any type

In this example, we will see that we can create a listing of anything, including objects.

use Khalyomede\ReorderBeforeAfter\Item;
use Khalyomede\ReorderBeforeAfter\Listing;

final readonly class Product
{
    public function __construct(
        public string $name,
        public int $quantity,
        public float $unitPrice,
    ) {}
}

$bags = new Product("bag", 15, 149.99);
$tables = new Product("table", 4, 89.99);
$chairs = new Product("chairs", 1, 399.99);

$listing = new Listing();

$listing->push(new Item($bags, 1));
$listing->push(new Item($tables, 2));
$listing->push(new Item($chairs, 3));

3. Find an item by its value

In this example, we will see we can find an item by its "value" (the first parameter when creating a new instance of Item).

use Khalyomede\ReorderBeforeAfter\Item;
use Khalyomede\ReorderBeforeAfter\Listing;
use Khalyomede\ReorderBeforeAfter\Placement;

$listing = new Listing();

$listing->push(new Item("bag", 1));
$listing->push(new Item("chair", 2));
$listing->push(new Item("table", 3));
$listing->push(new Item("book", 4));

echo $listing->find("bag"); // Item(value: "bag", order: 1)

If the method finds no item matching the value, a ItemNotFoundException will be thrown.

You can also search by anything that can be checked using ===, including objects.

use Khalyomede\ReorderBeforeAfter\Item;
use Khalyomede\ReorderBeforeAfter\Listing;
use Khalyomede\ReorderBeforeAfter\Placement;

final readonly class Product
{
    public function __construct(
        public string $name,
        public int $quantity,
        public float $unitPrice,
    ) {}
}

$bags = new Product("bag", 15, 149.99);
$tables = new Product("table", 4, 89.99);
$chairs = new Product("chairs", 1, 399.99);

$listing = new Listing();

$listing->push(new Item($bags, 1));
$listing->push(new Item($tables, 2));
$listing->push(new Item($chairs, 3));

echo $listing->find($bags); // Item(value: Product(name: "bag", quantity: 15, unitPrice: 149.99), order: 1)

4. Create a listing from an array

In this example, we will create the listing from an array instead of manually pushing items.

use Khalyomede\ReorderBeforeAfter\Listing;

$listing = Listing::from([
    new Item("bag", 1),
    new Item("chair", 2),
    new Item("book", 3),
    new Item("table", 4),
]);

$listing->reorder("bag", Placement::After, "book");

assert($listing->find("bag")->order === 3);

5. Getting all items from a listing

In this example, we will get all items from a listing. Useful if you want to perform some task after reordering your items.

use Khalyomede\ReorderBeforeAfter\Listing;

$listing = Listing::from([
    new Item("bag", 1),
    new Item("chair", 2),
    new Item("book", 3),
    new Item("table", 4),
]);

$products = $listing->all();

foreach ($products as $product) {
    echo $product; // "bag" or "chair" or "book" or "table"
}

6. Use a callback to apply the order on your value

In this example, we will instruct the listing how it should set the order on our values. Useful if our values are objects holding their order. This saves you from looping again on all your objects.

This examples features an hypothetical Product Eloquent model.

use App\Models\Product;
use Khalyomede\ReorderBeforeAfter\Listing;

$items = Product::all()->map(fn (Product $product): Item => new Item($product, $product->order));
$listing = Listing::from($items);

$listing->applyWith(function (Item $item): void {
    $item->value->order = $item->order;
    $item->value->save();
});

7. Create a listing from values and specify how to retrieve the order using a callback

In this example, we will see how to create a listing out of an array of values, and using the second parameter to specify how to get the order from these values.

Useful if you have objects that already hold their own order, and you do not want to loop from them and create the Item by hand.

use App\Models\Product;
use Khalyomede\ReorderBeforeAfter\Listing;

$products = Product::all();
$listing = Listing::outOf($products, fn (Product $product): Item => new Item($product, $product->order));

8. Specify how to match items together

In this example, we will instruct the listing how it should match our objects. Useful if your objects can contain different set of data, making the whole object different, when in reality both are equal (matching by id).

This examples features an hypothetical Product Eloquent model, where one of the two can have more attributes (like eager loaded relationships), making the triple equal check invalid.

use App\Models\Product;
use Khalyomede\ReorderBeforeAfter\Listing;

$listing = Listing::outOf(Product::all(), fn (Product $product): Item => new Item($product, $product->order));

$listing->matchWith(fn (Product $left, Product $right): bool => $left->id === $right->id);

// Or with the shorter Eloquent::is() method
$listing->matchWith(fn (Product $left, Product $right): bool => $left->is($right));

Tests

composer run test
composer run analyse
composer run lint
composer run check
composer run updates
composer run scan

Or

composer run all

reorder-before-after's People

Contributors

khalyomede avatar

Watchers

 avatar

reorder-before-after's Issues

Change reorder function to take values instead of Item

To make the API easier, we should be able to pass the value directly instead of saving an instance of the item.

This

use Khalyomede\ReorderBeforeAfter\Listing;
use Khalyomede\ReorderBeforeAfter\Placement;

$bag = new Item("bag", 1);
$book = new Item("book, 2);

$listing = new Listing([$bag, $book]);

$listing->reorder($bag, Placement::After, $book);

Would become this

use Khalyomede\ReorderBeforeAfter\Listing;
use Khalyomede\ReorderBeforeAfter\Placement;

$listing = new Listing([new Item("bag", 1), new Item("book", 2)]);

$listing->reorder("bag", Placement::After, "book");

Ability to create a listing from a list of items and specifying a callback to find the order of the items

For example instead of this:

use Khalyomede\ReorderBeforeAfter\Listing;
use App\Models\Product;

$items = Product::all()->map(fn (Product $product): Item => new Item($product, $product->order));
$listing = Listing::from($items);

We could have this, which is reducing the redunduncy if the list of objects comes from a previous call:

use Khalyomede\ReorderBeforeAfter\Listing;
use App\Models\Product;

$listing = Listing::outOf(Product::all(), fn (Product $product): Item => new Item($product, $product->order);

Specify how items should match another

Currently, I use Laravel Eloquent models, and on my controller I match a model with another that have more relationships loaded.

Currently, the triple equal comparison fails (the one that is currently used under the hood of this package). Which leads to issues when an item is not found when in fact it is in the list of items, because Eloquent models with not exactly the same relationships loaded are considered not equal. See below

$product = Product::first();
$same = Product::with("supplier")->first();

assert(($product->id === $other->id) === true);
assert($product->is($same) === true);
assert(($product === $other) === false)

That's why I propose a new method to specify how to match items together:

use App\Models\Product;
use Khalyomede\ReorderBeforeAfter;

$listing = Listing::outOf(Product::all(), fn (Product $product): Item => new Item($product, $product->order);
$listing->matchWith(fn (Product $left, Product $right): bool => $left->is($right));

Add ability to specify a callback to apply the order on the item

Useful when we have an object that contains its order, and we want to update it accordingly.

Instead of this:

use Khalyomede\ReorderBeforeAfter\Item;
use Khalyomede\ReorderBeforeAfter\Listing;
use Khalyomede\ReorderBeforeAfter\Placement;

final readonly class Product
{
    public function __construct(
        public string $name,
        public int $quantity,
        public float $unitPrice,
        public int $order,
    ) {}
}

$bags = new Product("bag", 15, 149.99, 1);
$tables = new Product("table", 4, 89.99, 2);
$chairs = new Product("chairs", 1, 399.99, 3);

$listing = new Listing();

$listing->push(new Item($bags, $bags->order));
$listing->push(new Item($tables, $tables->order));
$listing->push(new Item($chairs, $chairs->order));

$listing->reorder($chairs, Placement::Before, $tables);

foreach ($listing->items() as $item) {
  $item->value->order = $item->order;
}

We could have this

use Khalyomede\ReorderBeforeAfter\Item;
use Khalyomede\ReorderBeforeAfter\Listing;
use Khalyomede\ReorderBeforeAfter\Placement;

final readonly class Product
{
    public function __construct(
        public string $name,
        public int $quantity,
        public float $unitPrice,
        public int $order,
    ) {}
}

$bags = new Product("bag", 15, 149.99, 1);
$tables = new Product("table", 4, 89.99, 2);
$chairs = new Product("chairs", 1, 399.99, 3);

$listing = new Listing();

$listing->push(new Item($bags, $bags->order));
$listing->push(new Item($tables, $tables->order));
$listing->push(new Item($chairs, $chairs->order));

$listing->applyWith(function (Product $product, int $order): void {
    $product->order = $order;
    $product->save();
});

$listing->reorder($chairs, Placement::Before, $tables);
$listing->applyOrder(); // Will save all models

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.