Downloads
Stars
Version
Turn any Eloquent model into a list!
:zap: Inactivity Notice ⚠️ |
---|
Thanks for your interest. I no longer use PHP or Laravel and do not have the bandwidth to maintain this package.
Listify
provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a position
column defined as an integer on the mapped database table. Listify
is an Eloquent port of the highly useful Ruby gem acts_as_list
(https://github.com/swanandp/acts_as_list).
Listify
currently requires php >= 5.5 (Listify
is implemented via the use of traits).For use with Laravel 4, please use version 1.0.6.
Listify
is distributed as a composer package, which is how it should be used in your app.
Install the package using Composer. Edit your project's composer.json
file to require lookitsatravis/listify
.
"require": {
"laravel/framework": "~5.0",
"lookitsatravis/listify": "~1.2"
}
Once this operation completes, the final step is to add the service provider. Open app/config/app.php
, and add a new item to the providers array.
'Lookitsatravis\Listify\ListifyServiceProvider'
Optionally, you can define an alias to the Listify
trait. Open app/config/app.php
, and add a new item to the aliases array.
'Listify' => 'Lookitsatravis\Listify\Listify'
First things first, you'll need to add a column to store the position. From the command line, use the migration generator:
php artisan listify:attach {table_name} {position_column_name}
php artisan migrate
{table_name}
is a required argument.{position_column_name}
is optional and the default value is "position".
Then, in your model:
class User extends Eloquent
{
use \Lookitsatravis\Listify\Listify;
public function __construct(array $attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->initListify();
}
}
Make sure that the
initListify()
method is called afterparent::__construct()
of your model.
That's all it takes to get access to the Listify
hotness.
You'll have a number of methods added to each instance of the Eloquent model to which Listify
is added.
In Listify
, "higher" means further up the list (a lower position
), and "lower" means further down the list (a higher position
). That can be confusing, so it might make sense to add tests that validate that you're using the right method given your context.
eloquentModel.insertAt(2)
eloquentModel.moveLower()
will do nothing if the item is the lowest itemeloquentModel.moveHigher()
will do nothing if the item is the highest itemeloquentModel.moveToBottom()
eloquentModel.moveToTop()
eloquentModel.removeFromList()
eloquentModel.incrementPosition()
eloquentModel.decrementPosition()
eloquentModel.setListPosition(3)
eloquentModel.isFirst()
eloquentModel.isLast()
eloquentModel.isInList()
eloquentModel.isNotInList()
eloquentModel.isDefaultPosition()
eloquentModel.higherItem()
eloquentModel.higherItems()
will return all the items above eloquentModel
in the list (ordered by the position, ascending)eloquentModel.lowerItem()
eloquentModel.lowerItems()
will return all the items below eloquentModel
in the list (ordered by the position, ascending)##Configuration
There are a few configuration options available. You'll need to pass these in as an array argument for initListify()
in your model's constructor. Here are the options:
top_of_list
sets the integer position for the top of the list (default: 1
).column
sets the name of your position column that you chose during installation (default: 'position'
).add_new_at
sets the name of your position column that you chose during installation (default: 'bottom'
, options: 'top'
or 'bottom'
).scope
allows you to scope the items in your list. This one requires a bit of elaboration. There are three posible values accepted:
string
Illuminate\Database\Eloquent\Relations\BelongsTo
objectIlluminate\Database\Query\Builder
object###String
If string
is passed in, a raw string is passed in as a whereRaw
to the scope. This allows you to do something like 'custom_foreign_key = 42'
and have all of the items scoped to that result set. You can pass as complicated of a where clause as you want, and it will be passed straight into each DB operation.
Example:
class User extends Eloquent
{
use \Lookitsatravis\Listify\Listify;
public function __construct(array $attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->initListify([
'scope' => 'answer_to_ltuae = 42'
]);
}
}
Results in a scope of:
WHERE answer_to_ltuae = 42
###Illuminate\Database\Eloquent\Relations\BelongsTo
If Illuminate\Database\Eloquent\Relations\BelongsTo
is passed in, Listify
will match up the foreign key of the scope to the value of the corresponding foreign key of the model instance.
Example:
class ToDoListItem extends Eloquent
{
use \Lookitsatravis\Listify\Listify;
public function __construct(array $attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->initListify([
'scope' => $this->toDoList()
]);
}
public function toDoList()
{
$this->belongsTo('ToDoList');
}
}
Results in a scope of:
WHERE to_do_list_id = {{value of toDoListItem.to_do_list_id}}
###Illuminate\Database\Query\Builder
And lastly, if Illuminate\Database\Query\Builder
is passed in, Listify
will extract the where clause of the builder and use it as the scope of the Listify
items. This scope type was added in an attempt to keep parity between the acts_as_list
version and Listify
; however, due to differences in the languages and in ActiveRecord versus Eloquent, it is a limited implementation so far and needs impovement to be more flexible and secure. This is a big limitation and will be the first thing addressed in upcoming releases.
This one is tricky, because in order for it to work the query objects where
array is prepared with the bindings outside of PDO and then passed in as a raw string. So, please keep in mind that this route can open your application up to abuse if you are not careful about how the object is built. If you use direct user input, please sanitize the data before using this as a scope for Listify
.
Example:
class ToDoListItem extends Eloquent
{
use \Lookitsatravis\Listify\Listify;
public function __construct(array $attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->initListify([
'scope' => DB::table($this->getTable())->where('type', '=', 'Not A List of My Favorite Porn Videos')
]);
}
}
Results in a scope of:
to_do_list_items.type = 'Not A List of My Favorite Porn Videos'
You may also change any configuration value during runtime by using $this->setListifyConfig('key', 'value');
. For example, to change the scope, you can do this:
$this->setListifyConfig('scope', 'what_does_the_fox_say = "ring ding ding ding"');
When an update is processed, the original scope will be used to remove the record from that list, and insert it into the new list scope. Be careful here. Changing the configuration during processing can have unpredictable effects.
All position
queries (select, update, etc.) inside trait methods are executed without the default scope, this will prevent nasty issues when the default scope is different from Listify
scope.
The position
column is set after validations are called, so you should not put a presence
validation on the position
column.
Illuminate\Database\Query\Builder
scope to be more secure and flexibleListify
, including adding necessary routes. This would make it easy to, say, call something like http://localhost:8000/foos/1/move_lower
through an AJAX-y front end.Aside from that, I hope to just keep in parity with the Ruby gem acts_as_list
(https://github.com/swanandp/acts_as_list) as necessary.
Listify
Copyright (c) 2013-2017 Travis Vignon, released under the MIT license