class-query.php
<?php
namespace HivePress\Queries;
use HivePress\Helpers as hp;
use HivePress\Traits;
defined( 'ABSPATH' ) || exit;
abstract class Query extends \ArrayObject {
use Traits\Mutator;
protected $aliases = [];
protected $args = [];
protected $model;
protected $executed = false;
public function __construct( $args = [] ) {
$args = hp\merge_arrays(
[
'aliases' => [
'search' => 'search',
'limit' => 'number',
'offset' => 'offset',
'paginate' => 'paged',
'select' => [
'name' => 'fields',
'aliases' => [
'id' => 'ids',
],
],
'aggregate' => [
'name' => 'aggregate',
'aliases' => [
'count' => 'count',
],
],
'filter' => [
'name' => 'filter',
],
'order' => [
'name' => 'orderby',
],
],
],
$args
);
foreach ( $args as $name => $value ) {
$this->set_property( $name, $value );
}
$this->boot();
}
protected function boot() {}
final protected function get_alias( $path ) {
$alias = [ 'aliases' => $this->aliases ];
foreach ( explode( '/', $path ) as $name ) {
if ( isset( $alias['aliases'][ $name ] ) ) {
$alias = $alias['aliases'][ $name ];
} else {
return;
}
}
if ( is_array( $alias ) ) {
$alias = hp\get_array_value( $alias, 'name' );
}
return $alias;
}
final protected function get_operator( $alias ) {
return hp\get_array_value(
[
'not' => '!=',
'gt' => '>',
'gte' => '>=',
'lt' => '<',
'lte' => '<=',
'like' => 'LIKE',
'not_like' => 'NOT LIKE',
'in' => 'IN',
'not_in' => 'NOT IN',
'between' => 'BETWEEN',
'not_between' => 'NOT BETWEEN',
'exists' => 'EXISTS',
'not_exists' => 'NOT EXISTS',
],
strtolower( $alias ),
'='
);
}
final public function set_args( $args ) {
$this->args = hp\merge_arrays( $this->args, $args );
return $this;
}
final public function get_args() {
return $this->args;
}
public function filter( $criteria ) {
foreach ( $criteria as $name => $value ) {
$name = strtolower( $name );
if ( $this->get_alias( 'filter/' . $name ) ) {
$this->args[ $this->get_alias( 'filter/' . $name ) ] = $value;
} else {
$operator_alias = '';
if ( strpos( $name, '__' ) ) {
list($name, $operator_alias) = explode( '__', $name );
}
$field = hp\get_array_value( $this->model->_get_fields(), $name );
if ( $field ) {
if ( $field->get_arg( '_external' ) ) {
$operator = $this->get_operator( $operator_alias );
$clause = [
'key' => $field->get_arg( '_alias' ),
'compare' => $operator,
];
if ( ! in_array( $operator, [ 'EXISTS', 'NOT EXISTS' ], true ) ) {
if ( is_bool( $value ) ) {
$value = $value ? '1' : null;
}
if ( is_null( $value ) ) {
$clause['compare'] = 'NOT EXISTS';
} else {
$clause = array_merge(
$clause,
[
'type' => $field::get_meta( 'type' ),
'value' => $value,
]
);
}
}
if ( empty( $operator_alias ) ) {
$operator_alias = 'equals';
}
$this->args['meta_query'][ $name . '__' . $operator_alias ] = $clause;
} elseif ( $field->get_arg( '_alias' ) && ! $field->get_arg( '_relation' ) ) {
if ( ! in_array( $operator_alias, [ 'in', 'not_in', 'like' ], true ) ) {
$operator_alias = '';
}
$this->args[ rtrim( $field->get_arg( '_alias' ) . '__' . $operator_alias, '_' ) ] = $value;
}
}
}
}
return $this;
}
public function order( $criteria ) {
$args = [];
if ( is_array( $criteria ) ) {
foreach ( $criteria as $name => $order ) {
$order = strtoupper( $order );
if ( in_array( $order, [ 'ASC', 'DESC' ], true ) ) {
if ( $this->get_alias( 'order/' . $name ) ) {
$args[ $this->get_alias( 'order/' . $name ) ] = $order;
} else {
$field = hp\get_array_value( $this->model->_get_fields(), $name );
if ( $field ) {
if ( $field->get_arg( '_external' ) ) {
$field->update_filter( true );
$filter = [
'key' => $field->get_arg( '_alias' ),
'type' => hp\get_array_value( $field->get_filter(), 'type' ),
];
$this->args['meta_query'][] = [
'relation' => 'OR',
$name . '__order' => array_merge(
$filter,
[
'compare' => 'NOT EXISTS',
]
),
array_merge(
$filter,
[
'compare' => 'EXISTS',
]
),
];
$args[ $name . '__order' ] = $order;
} elseif ( $field->get_arg( '_alias' ) && ! $field->get_arg( '_relation' ) ) {
$args[ $field->get_arg( '_alias' ) ] = $order;
}
}
}
}
}
} elseif ( $this->get_alias( 'order/' . $criteria ) ) {
$args = $this->get_alias( 'order/' . $criteria );
}
if ( $args ) {
$this->args[ $this->get_alias( 'order' ) ] = $args;
}
return $this;
}
public function search( $query ) {
$this->args[ $this->get_alias( 'search' ) ] = $query;
return $this;
}
public function limit( $number ) {
$this->args[ $this->get_alias( 'limit' ) ] = absint( $number );
return $this;
}
public function offset( $number ) {
$this->args[ $this->get_alias( 'offset' ) ] = absint( $number );
return $this;
}
public function paginate( $number ) {
$this->args[ $this->get_alias( 'paginate' ) ] = absint( $number );
return $this;
}
abstract protected function get_results( $args );
final public function get() {
if ( ! $this->executed ) {
$this->exchangeArray(
array_map(
function( $result ) {
return $this->model->get( $result );
},
$this->get_results( $this->args )
)
);
$this->executed = true;
}
return $this;
}
public function get_ids() {
$ids = [];
if ( $this->executed ) {
$ids = array_map(
function( $object ) {
return $object->get_id();
},
$this->serialize()
);
} else {
$ids = array_map(
'absint',
$this->get_results(
array_merge(
$this->args,
[
$this->get_alias( 'select' ) => $this->get_alias( 'select/id' ),
]
)
)
);
}
return $ids;
}
final public function get_first() {
$object = null;
if ( $this->executed ) {
$object = hp\get_first_array_value( $this->serialize() );
} else {
$query = clone $this;
$object = hp\get_first_array_value( $query->limit( 1 )->get()->serialize() );
}
return $object;
}
final public function get_first_id() {
$id = null;
if ( $this->executed ) {
$id = hp\get_first_array_value( $this->get_ids() );
} else {
$query = clone $this;
$id = hp\get_first_array_value( $query->limit( 1 )->get_ids() );
}
return $id;
}
final public function get_by_id( $id ) {
return $this->model->get( $id );
}
public function get_count() {
$count = 0;
if ( $this->executed ) {
$count = count( $this->get_ids() );
} else {
$count = $this->get_results(
array_merge(
$this->args,
[
$this->get_alias( 'aggregate/count' ) => true,
]
)
);
}
return $count;
}
final public function delete() {
foreach ( $this->get_ids() as $id ) {
$this->delete_by_id( $id );
}
}
final public function delete_by_id( $id ) {
$this->model->delete( $id );
}
final public function serialize() {
return $this->getArrayCopy();
}
}