Commit 435456cb authored by endi's avatar endi

Init

parents
/vendor
composer.phar
composer.lock
.DS_Store
language: php
php:
- 5.4
- 5.5
- 5.6
- hhvm
before_script:
- travis_retry composer self-update
- travis_retry composer install --prefer-source --no-interaction --dev
script: phpunit
# Installation
add in composer
```json
"repositories": [
{
"type": "vcs",
"url": "git@gitlab.top.md:terranet/rankable.git"
}
],
"require": {
"terranet/rankable": "dev-master"
}
```
```sh
composer update
```
### Usage example
```php
class Program extends Repository implements Translatable, Rankable
{
use HasRankableField;
// optional
protected $rankableColumn = 'rank';
protected $rankableIncrementValue = 2;
protected $rankableGroupByColumn = 'member_id';
}
```
now Program::all() will contain orderBy('rank', 'asc') statement
to disable default orderBy statement use:
```php
Program::unRanked()
```
to sync rankings call:
```php
$model = new Program;
$model->syncRanking([
1 => 3,
2 => 2,
3 => 1
]);
```
where key => id, value => rank
\ No newline at end of file
{
"name": "terranet/rankable",
"description": "Provides useful methods to make an eloquent collection rankable",
"keywords": ["laravel", "rankable", "eloquent"],
"authors": [
{
"name": "Endi Hunter",
"email": "endi@terranet.md"
}
],
"require": {
"php": ">=5.4.0",
"illuminate/database": "~5.0"
},
"autoload": {
"psr-0": {
"Terranet\\Rankable": "src/"
}
},
"minimum-stability": "stable"
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
<?php namespace Terranet\Rankable;
trait HasRankableField
{
/**
* Get the rankable field name
*
* @return mixed
*/
public function getRankableColumn()
{
return (isset($this->rankableColumn) ? $this->rankableColumn : 'rank');
}
/**
* Get the rankable field name
*
* @return mixed
*/
public function getRankableIncrementValue()
{
return (isset($this->rankableIncrementValue) ? (int) $this->rankableIncrementValue : 1);
}
/**
* Calculate new rank value based on records group
*
* @ex.: Group comments rank by post_id
*
* @return array|null
*/
public function getRankableGroupByColumn()
{
return (isset($this->rankableGroupByColumn) ? (array) $this->rankableGroupByColumn : null);
}
/**
* Synchronize ranking
*
* @param array $ranking - assoc array where key = record id, value = record rank value
* @return mixed
*/
public function syncRanking(array $ranking = [])
{
foreach((array) $ranking as $id => $value)
{
$this
->where($this->getKeyName(), '=', $id)
->update([
$this->getRankableColumn() => (int) $value
]);
}
}
public static function bootHasRankableField()
{
static::addGlobalScope(new RankableScope);
static::updating(function(Rankable $model)
{
if ($model->isDirty($model->getRankableColumn()) && $oldValue = $model->getOriginal($model->getRankableColumn()))
{
// switch ranks in conflicts
$query = $model->newQuery();
$query = $model->applyRankGrouping($query, $model);
$query->where($model->getRankableColumn(), '=', (int) $model->getAttribute($model->getRankableColumn()));
$query->update([
$model->getRankableColumn() => $oldValue
]);
}
});
static::creating(function(Rankable $model)
{
$field = $model->getRankableColumn();
if (! $model->{$field})
{
$query = $model->newQuery();
$query = $model->applyRankGrouping($query, $model);
$value = (int) $query->max($field);
$model->{$field} = ($value + $model->getRankableIncrementValue());
}
});
}
protected function applyRankGrouping($query, $model)
{
if ($columns = (array) $model->getRankableGroupByColumn())
{
foreach($columns as $column)
{
$query->where($column, '=', $model->{$column});
}
}
return $query;
}
}
\ No newline at end of file
<?php namespace Terranet\Rankable;
interface Rankable
{
/**
* Get the sortable field name
*
* @return mixed
*/
public function getRankableColumn();
/**
* Get incrementing value
*
* @return mixed
*/
public function getRankableIncrementValue();
/**
* Calculate new rank value based on records group
*
* @ex.: Group comments rank by post_id
*
* @return array|null
*/
public function getRankableGroupByColumn();
/**
* Synchronize ranking
*
* @param array $ranking - assoc array where key = record id, value = record rank value
* @return mixed
*/
public function syncRanking(array $ranking = []);
}
\ No newline at end of file
<?php namespace Terranet\Rankable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ScopeInterface;
class RankableScope implements ScopeInterface
{
protected $extensions = ['Unrank'];
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->orderBy($model->getRankableColumn());
$this->extend($builder);
}
/**
* Remove the scope from the given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return void
*/
public function remove(Builder $builder, Model $model)
{
$column = $model->getRankableColumn();
$query = $builder->getQuery();
foreach ((array) $query->orders as $key => $direction)
{
// If the where clause is a soft delete date constraint, we will remove it from
// the query and reset the keys on the wheres. This allows this developer to
// include deleted model in a relationship result set that is lazy loaded.
if ($this->isRankableOrder($key, $column))
{
unset($query->orders[$key]);
$query->orders = array_values($query->orders);
}
}
}
/**
* Extend the query builder with the needed functions.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
public function extend(Builder $builder)
{
foreach ($this->extensions as $extension)
{
$this->{"add{$extension}"}($builder);
}
}
/**
* Unrank .
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
protected function addUnrank(Builder $builder)
{
$builder->macro('unRanked', function(Builder $builder)
{
$this->remove($builder, $builder->getModel());
return $builder;
});
}
private function isRankableOrder($key, $column)
{
return $column == $key;
}
}
\ No newline at end of file
<?php namespace Terranet\Rankable;
use Illuminate\Support\ServiceProvider;
class RankableServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [];
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment