Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Baum

[![Build Status](https://travis-ci.org/etrepat/baum.png?branch=master)](https://travis-ci.org/etrepat/baum)
[![StyleCI](https://styleci.io/repos/51155875/shield)](https://styleci.io/repos/51155875)

Baum is an implementation of the [Nested Set](http://en.wikipedia.org/wiki/Nested_set_model) pattern for [Laravel 5's](http://laravel.com/) Eloquent ORM.

Expand Down
68 changes: 42 additions & 26 deletions src/Baum/Extensions/Eloquent/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,55 @@

use Illuminate\Database\Eloquent\Collection as BaseCollection;

class Collection extends BaseCollection {

public function toHierarchy() {
$dict = $this->getDictionary();
/**
* Class Collection
* @package Baum\Extensions\Eloquent
*/
class Collection extends BaseCollection
{

/**
* @return BaseCollection
*/
public function toHierarchy()
{
$dict = $this->getDictionary();

// Enforce sorting by $orderColumn setting in Baum\Node instance
uasort($dict, function ($a, $b) {
return ($a->getOrder() >= $b->getOrder()) ? 1 : -1;
});

return new BaseCollection($this->hierarchical($dict));
}

// Enforce sorting by $orderColumn setting in Baum\Node instance
uasort($dict, function($a, $b){
return ($a->getOrder() >= $b->getOrder()) ? 1 : -1;
});
/**
* @param $result
* @return mixed
*/
protected function hierarchical($result)
{
foreach ($result as $key => $node) {
$node->setRelation('children', new BaseCollection);
}

return new BaseCollection($this->hierarchical($dict));
}
$nestedKeys = array();

protected function hierarchical($result) {
foreach($result as $key => $node)
$node->setRelation('children', new BaseCollection);
foreach ($result as $key => $node) {
$parentKey = $node->getParentId();

$nestedKeys = array();
if (!is_null($parentKey) && array_key_exists($parentKey, $result)) {
$result[$parentKey]->children[] = $node;

foreach($result as $key => $node) {
$parentKey = $node->getParentId();
$nestedKeys[] = $node->getKey();
}
}

if ( !is_null($parentKey) && array_key_exists($parentKey, $result) ) {
$result[$parentKey]->children[] = $node;
foreach ($nestedKeys as $key) {
unset($result[$key]);
}

$nestedKeys[] = $node->getKey();
}
return $result;
}

foreach($nestedKeys as $key)
unset($result[$key]);

return $result;
}

}
227 changes: 120 additions & 107 deletions src/Baum/Extensions/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,114 +6,127 @@
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Baum\Extensions\Query\Builder as QueryBuilder;

abstract class Model extends BaseModel {

/**
* Reloads the model from the database.
*
* @return \Baum\Node
*
* @throws ModelNotFoundException
*/
public function reload() {
if ( $this->exists || ($this->areSoftDeletesEnabled() && $this->trashed()) ) {
$fresh = $this->getFreshInstance();

if ( is_null($fresh) )
throw with(new ModelNotFoundException)->setModel(get_called_class());

$this->setRawAttributes($fresh->getAttributes(), true);

$this->setRelations($fresh->getRelations());

$this->exists = $fresh->exists;
} else {
// Revert changes if model is not persisted
$this->attributes = $this->original;
abstract class Model extends BaseModel
{

/**
* Reloads the model from the database.
*
* @return \Baum\Node
*
* @throws ModelNotFoundException
*/
public function reload()
{
if ($this->exists || ($this->areSoftDeletesEnabled() && $this->trashed())) {
$fresh = $this->getFreshInstance();

if (is_null($fresh)) {
throw with(new ModelNotFoundException)->setModel(get_called_class());
}

$this->setRawAttributes($fresh->getAttributes(), true);

$this->setRelations($fresh->getRelations());

$this->exists = $fresh->exists;
} else {
// Revert changes if model is not persisted
$this->attributes = $this->original;
}

return $this;
}

return $this;
}

/**
* Get the observable event names.
*
* @return array
*/
public function getObservableEvents() {
return array_merge(array('moving', 'moved'), parent::getObservableEvents());
}

/**
* Register a moving model event with the dispatcher.
*
* @param Closure|string $callback
* @return void
*/
public static function moving($callback, $priority = 0) {
static::registerModelEvent('moving', $callback, $priority);
}

/**
* Register a moved model event with the dispatcher.
*
* @param Closure|string $callback
* @return void
*/
public static function moved($callback, $priority = 0) {
static::registerModelEvent('moved', $callback, $priority);
}

/**
* Get a new query builder instance for the connection.
*
* @return \Baum\Extensions\Query\Builder
*/
protected function newBaseQueryBuilder() {
$conn = $this->getConnection();

$grammar = $conn->getQueryGrammar();

return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
}

/**
* Returns a fresh instance from the database.
*
* @return \Baum\Node
*/
protected function getFreshInstance() {
if ( $this->areSoftDeletesEnabled() )
return static::withTrashed()->find($this->getKey());

return static::find($this->getKey());
}

/**
* Returns wether soft delete functionality is enabled on the model or not.
*
* @return boolean
*/
public function areSoftDeletesEnabled() {
// To determine if there's a global soft delete scope defined we must
// first determine if there are any, to workaround a non-existent key error.
$globalScopes = $this->getGlobalScopes();

if ( count($globalScopes) === 0 ) return false;

// Now that we're sure that the calling class has some kind of global scope
// we check for the SoftDeletingScope existance
return static::hasGlobalScope(new SoftDeletingScope);
}

/**
* Static method which returns wether soft delete functionality is enabled
* on the model.
*
* @return boolean
*/
public static function softDeletesEnabled() {
return with(new static)->areSoftDeletesEnabled();
}
/**
* Get the observable event names.
*
* @return array
*/
public function getObservableEvents()
{
return array_merge(array('moving', 'moved'), parent::getObservableEvents());
}

/**
* Register a moving model event with the dispatcher.
*
* @param Closure|string $callback
* @return void
*/
public static function moving($callback, $priority = 0)
{
static::registerModelEvent('moving', $callback, $priority);
}

/**
* Register a moved model event with the dispatcher.
*
* @param Closure|string $callback
* @return void
*/
public static function moved($callback, $priority = 0)
{
static::registerModelEvent('moved', $callback, $priority);
}

/**
* Get a new query builder instance for the connection.
*
* @return \Baum\Extensions\Query\Builder
*/
protected function newBaseQueryBuilder()
{
$conn = $this->getConnection();

$grammar = $conn->getQueryGrammar();

return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
}

/**
* Returns a fresh instance from the database.
*
* @return \Baum\Node
*/
protected function getFreshInstance()
{
if ($this->areSoftDeletesEnabled()) {
return static::withTrashed()->find($this->getKey());
}

return static::find($this->getKey());
}

/**
* Returns wether soft delete functionality is enabled on the model or not.
*
* @return boolean
*/
public function areSoftDeletesEnabled()
{
// To determine if there's a global soft delete scope defined we must
// first determine if there are any, to workaround a non-existent key error.
$globalScopes = $this->getGlobalScopes();

if (count($globalScopes) === 0) {
return false;
}

// Now that we're sure that the calling class has some kind of global scope
// we check for the SoftDeletingScope existance
return static::hasGlobalScope(new SoftDeletingScope);
}

/**
* Static method which returns wether soft delete functionality is enabled
* on the model.
*
* @return boolean
*/
public static function softDeletesEnabled()
{
return with(new static)->areSoftDeletesEnabled();
}

}
68 changes: 37 additions & 31 deletions src/Baum/Extensions/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,42 @@

use Illuminate\Database\Query\Builder as BaseBuilder;

class Builder extends BaseBuilder {

/**
* Replace the "order by" clause of the current query.
*
* @param string $column
* @param string $direction
* @return \Illuminate\Database\Query\Builder|static
*/
public function reOrderBy($column, $direction = 'asc') {
$this->orders = null;

if ( !is_null($column) ) return $this->orderBy($column, $direction);

return $this;
}

/**
* Execute an aggregate function on the database.
*
* @param string $function
* @param array $columns
* @return mixed
*/
public function aggregate($function, $columns = array('*')) {
// Postgres doesn't like ORDER BY when there's no GROUP BY clause
if ( !isset($this->groups) )
$this->reOrderBy(null);

return parent::aggregate($function, $columns);
}
class Builder extends BaseBuilder
{

/**
* Replace the "order by" clause of the current query.
*
* @param string $column
* @param string $direction
* @return \Baum\Extensions\Query\Builder|static
*/
public function reOrderBy($column, $direction = 'asc')
{
$this->orders = null;

if (!is_null($column)) {
return $this->orderBy($column, $direction);
}

return $this;
}

/**
* Execute an aggregate function on the database.
*
* @param string $function
* @param array $columns
* @return mixed
*/
public function aggregate($function, $columns = array('*'))
{
// Postgres doesn't like ORDER BY when there's no GROUP BY clause
if (!isset($this->groups)) {
$this->reOrderBy(null);
}

return parent::aggregate($function, $columns);
}

}
Loading