Skip to content

Instantly share code, notes, and snippets.

@ethaizone
Last active November 2, 2016 05:21
Show Gist options
  • Save ethaizone/b1c57b97de64656c684d1f24c413a203 to your computer and use it in GitHub Desktop.
Save ethaizone/b1c57b97de64656c684d1f24c413a203 to your computer and use it in GitHub Desktop.
NumberNotation for Eloquent. If you need store data from checkbox and need to query it. This guy is hero. Don't store as stupid json.
<?php
// Migration
Schema::create('example_models', function(Blueprint $table)
{
$table->increments('id');
// Just create it as integer
// My code will do magic for you.
$table->integer('channel')->unsigned()->index();
$table->timestamps();
});
// Query where
$collection = app(App\Models\ExampleModel)->whereNumberNotation('channel', ['app', 'mobile_web'])->get();
// Update data
$model = $collection->first();
$model->channel = ['web', 'api'];
$model->save();
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ExampleModel extends Model
{
use Traits\NumberNotation;
public function setChannelAttribute($value)
{
$this->attributes['channel'] = $this->convertSlugToNumberNotation('channel', $value);
}
public function getChannelAttribute($value)
{
return $this->convertNumberNotationToSlug('channel', $value);
}
public function getChannelNumberNotationSlugs()
{
// Don't change order of current list or else number notation will gone wrong.
// You can add new value to this via append to last item.
return [
'app',
'web',
'mobile_web',
'api',
'pos',
];
}
}
<?php
namespace App\Models\Traits;
use Exception;
/**
* This is trait for use with eloquent model only.
* Develop by Nimit Suwannagate <[email protected]>
* I don't allow to modify anythings in this code.
* Please respect my work via don't remove copyright.
* Thank.
*/
trait NumberNotation
{
protected $processedNumberNotations = [];
/**
* [GENERATOR] Create number notation.
* Fastest generator that I can think right now.
*
* @author Nimit Suwannagate <[email protected]>
* @param int $count
* @return Generator
*/
protected function numberNotationGenerator($count)
{
if (! is_numeric($count)){
throw new Exception("Count is not numeric");
}
$numberChunk = [];
while (true) {
$number = pow(2, count($numberChunk));
$numberChunk[] = $number;
yield $number;
if ($count == count($numberChunk)){
break;
}
}
}
/**
* Get number notation table
* @param string $field
* @return array
*/
public function getNumberNotationsByField($field)
{
// check in memory cache
if (empty($this->processedNumberNotations[$field])) {
// Try call method that content base slug table.
// This method must return array.
// !! WARNING!!
// You can append new slug to last position in array
// but you must not insert before old slug
// or move or reorder current item in slug table.
// This will make result from this code will gone wrong.
$method = 'get'.ucfirst($field).'NumberNotationSlugs';
$mapping = call_user_func_array([$this, $method], []);
if (! is_array($mapping)) {
throw new Exception("Result from " . $method . " must be array.");
}
// use my generator to get value
$generator = $this->numberNotationGenerator(count($mapping));
// merge it to create list
$notations = [];
foreach ($generator as $index => $value) {
$notations[$value] = $mapping[$index];
}
$this->processedNumberNotations[$field] = $notations;
}
return $this->processedNumberNotations[$field];
}
/**
* Convert slug to single number notation.
* This one is safe for store to DB as integer
* @param string $field
* @param mixed $slug
* @return int
*/
public function convertSlugToNumberNotation($field, $slug)
{
// get number natation table
$numberNotations = $this->getNumberNotationsByField($field);
// flip it because we need index as slug
$numberNotations = array_flip($numberNotations);
// just make it to array if it's string
$slugs = (array) $slug;
// collect number
$number = 0;
foreach($slugs as $slug) {
if (!empty($numberNotations[$slug])) {
$number += $numberNotations[$slug];
}
}
return $number;
}
/**
* Convert number to slug
* @param string $field
* @param int $number
* @return array
*/
public function convertNumberNotationToSlug($field, $number)
{
// get number natation table
$numberNotations = $this->getNumberNotationsByField($field);
// we make reverse order because we need get slug from mostest value
krsort($numberNotations);
// protect number as int
$number = intval($number) ?: 0;
$slugs = [];
// loop until zero - stupid loop LOL.
while ($number > 0) {
foreach ($numberNotations as $value => $slug) {
if ($value > $number) {
// if number is low, we can safety discard value out.
unset($numberNotations[$value]);
} else {
// this number is first max in this loop.
// get it and go to next loop
$number -= $value;
$slugs[$value] = $slug;
break;
}
}
}
return $slugs;
}
/**
* Convert slug to number nocation member
* @param string $field
* @param mixed $slug
* @return array
*/
public function convertSlugToNumberMember($field, $slug)
{
// get number natation table
$numberNotations = $this->getNumberNotationsByField($field);
// flip it because we need index as slug
$numberNotations = array_flip($numberNotations);
// just make it to array if it's string
$slugs = (array) $slug;
// collect member
$member = [];
foreach($slugs as $slug) {
if (!empty($numberNotations[$slug])) {
$member[] = $numberNotations[$slug];
}
}
return $member;
}
/**
* Scope for where number notations in DB.
* We will use bitwise AND.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $field
* @param mixed $slug
*/
public function scopeWhereNumberNotation($query, $field, $slug)
{
// For logic to where data
// we will use bitwise for query record.
// it's awesome!!
// First we will change slugs to number notation member
$member = $this->convertSlugToNumberMember($field, $slug);
// let all member go to query as bitwise and
foreach($member as $number) {
$query->whereRaw($field.' & '.$number);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment