Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

+86 -67
View File
@@ -11,83 +11,99 @@
namespace IPS\Helpers\Table;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateTimeZone;
use Exception;
use IPS\DateTime;
use IPS\Db as IPSDb;
use IPS\Http\Url;
use IPS\Member;
use IPS\Request;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function is_array;
use function is_callable;
use function is_object;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* List Table Builder using a database table datasource
*/
class _Db extends Table
class Db extends Table
{
/**
* @brief Database Table
*/
protected $table;
protected ?string $table = NULL;
/**
* @brief Selects
*/
public $selects = array();
public array $selects = array();
/**
* @brief Initial WHERE clause
*/
public $where;
public mixed $where = NULL;
/**
* @brief Force index clause
*/
protected $index;
protected mixed $index = NULL;
/**
* @brief Primary sort column
*/
public $primarySortBy;
public ?string $primarySortBy = NULL;
/**
* @brief Direction of primary sort
*/
public $primarySortDirection;
public ?string $primarySortDirection = NULL;
/**
* @brief Joins
*/
public $joins = array();
public array $joins = array();
/**
* @brief Key field
*/
public $keyField = NULL;
public mixed $keyField = NULL;
/**
* @brief Group by key
*/
public $groupBy = NULL;
public string|array|null $groupBy = NULL;
/**
* @brief The database we will query against
*/
public $db;
public ?IPSDb $db = NULL;
/**
* Constructor
*
* @param string $table Database table
* @param \IPS\Http\Url $baseUrl Base URL
* @param array|null $where WHERE clause
* @param array|null $forceIndex Index to force
* @param \IPS\Db|null $database An instance of \IPS\Db to run the queries against (defaults to current connection)
* @return void
* @param string $table Database table
* @param Url $baseUrl Base URL
* @param array|string|null $where WHERE clause
* @param array|null $forceIndex Index to force
* @param IPSDb|null $database An instance of \IPS\Db to run the queries against (defaults to current connection)
*/
public function __construct( $table, \IPS\Http\Url $baseUrl, $where=NULL, $forceIndex=NULL, $database=NULL )
public function __construct( string $table, Url $baseUrl, array|string $where=null, mixed $forceIndex=null, IPSDb $database=null )
{
$this->table = $table;
$this->where = $where;
$this->index = $forceIndex;
$this->db = $database ?? \IPS\Db::i();
$this->db = $database ?? IPSDb::i();
return parent::__construct( $baseUrl );
}
@@ -96,31 +112,32 @@ class _Db extends Table
* Get rows
* @note This method is called twice, so if there'some expensive operation happening, or if you're calculating something, make sure to cache it.
*
* @param array $advancedSearchValues Values from the advanced search form
* @return array
* @param array|null $advancedSearchValues Values from the advanced search form
* @return array
* @throws Exception
*/
public function getRows( $advancedSearchValues )
public function getRows( array $advancedSearchValues = null ): array
{
/* Specify filter in where clause */
$where = $this->where ? \is_array( $this->where ) ? $this->where : array( $this->where ) : array();
$where = $this->where ? is_array( $this->where ) ? $this->where : array( $this->where ) : array();
if ( $this->filter and isset( $this->filters[ $this->filter ] ) )
{
$where[] = \is_array( $this->filters[ $this->filter ] ) ? $this->filters[ $this->filter ] : array( $this->filters[ $this->filter ] );
$where[] = is_array( $this->filters[ $this->filter ] ) ? $this->filters[ $this->filter ] : array( $this->filters[ $this->filter ] );
}
/* Add quick search term to where clause if necessary */
if ( $this->quickSearch !== NULL and \IPS\Request::i()->quicksearch )
if ( $this->quickSearch !== NULL and Request::i()->quicksearch )
{
if ( \is_callable( $this->quickSearch ) )
if ( is_callable( $this->quickSearch ) )
{
$quickSearchFunc = $this->quickSearch;
$where[] = $quickSearchFunc( trim( \IPS\Request::i()->quicksearch ) );
$where[] = $quickSearchFunc( trim( Request::i()->quicksearch ) );
}
else
{
$columns = \is_array( $this->quickSearch ) ? $this->quickSearch[0] : $this->quickSearch;
$columns = \is_array( $columns ) ? $columns : array( $columns );
$columns = is_array( $this->quickSearch ) ? $this->quickSearch[0] : $this->quickSearch;
$columns = is_array( $columns ) ? $columns : array( $columns );
$_where = array();
foreach ( $columns as $c )
@@ -128,7 +145,7 @@ class _Db extends Table
$_where[] = "LOWER(`{$c}`) LIKE CONCAT( '%', ?, '%' )";
}
$where[] = array_merge( array( '(' . implode( ' OR ', $_where ) . ')' ), array_fill( 0, \count( $_where ), mb_strtolower( trim( \IPS\Request::i()->quicksearch ) ) ) );
$where[] = array_merge( array( '(' . implode( ' OR ', $_where ) . ')' ), array_fill( 0, count( $_where ), mb_strtolower( trim( Request::i()->quicksearch ) ) ) );
}
}
@@ -137,11 +154,11 @@ class _Db extends Table
{
foreach ( $advancedSearchValues as $k => $v )
{
if ( isset( $this->advancedSearch[ $k ] ) AND $v !== '' AND ( !\is_array( $v ) OR !empty( $v ) ) )
if ( isset( $this->advancedSearch[ $k ] ) AND $v !== '' AND ( !is_array( $v ) OR !empty( $v ) ) )
{
$type = $this->advancedSearch[ $k ];
if ( \is_array( $type ) )
if ( is_array( $type ) )
{
if ( isset( $type[2] ) )
{
@@ -193,7 +210,7 @@ class _Db extends Table
}
break;
case SEARCH_DATE_RANGE:
$timezone = ( \IPS\Member::loggedIn()->timezone ? new \DateTimeZone( \IPS\Member::loggedIn()->timezone ) : NULL );
$timezone = ( Member::loggedIn()->timezone ? new DateTimeZone( Member::loggedIn()->timezone ) : NULL );
if( !$v['start'] AND !$v['end'] )
{
@@ -202,18 +219,18 @@ class _Db extends Table
if ( $v['start'] )
{
if( !( $v['start'] instanceof \IPS\DateTime ) )
if( !( $v['start'] instanceof DateTime ) )
{
$v['start'] = new \IPS\DateTime( $v['start'], $timezone );
$v['start'] = new DateTime( $v['start'], $timezone );
}
$where[] = array( "{$k}>?", $v['start']->getTimestamp() );
}
if ( $v['end'] )
{
if( !( $v['end'] instanceof \IPS\DateTime ) )
if( !( $v['end'] instanceof DateTime ) )
{
$v['end'] = new \IPS\DateTime( $v['end'], $timezone );
$v['end'] = new DateTime( $v['end'], $timezone );
}
$where[] = array( "{$k}<?", $v['end']->getTimestamp() );
@@ -235,7 +252,7 @@ class _Db extends Table
case SEARCH_MEMBER:
if ( $v )
{
$where[] = array( "{$k}=?", ( $v instanceof \IPS\Member ) ? $v->member_id : $v );
$where[] = array( "{$k}=?", ( $v instanceof Member ) ? $v->member_id : $v );
}
else
{
@@ -245,8 +262,8 @@ class _Db extends Table
case SEARCH_NODE:
$nodeClass = $options['class'];
$prop = isset( $options['searchProp'] ) ? $options['searchProp'] : '_id';
if ( !\is_array( $v ) )
$prop = $options['searchProp'] ?? '_id';
if ( !is_array( $v ) )
{
$v = array( $v );
}
@@ -254,7 +271,7 @@ class _Db extends Table
$values = array();
foreach ( $v as $_v )
{
if ( !\is_object( $_v ) )
if ( !is_object( $_v ) )
{
if ( mb_substr( $_v, 0, 2 ) === 's.' )
{
@@ -265,7 +282,7 @@ class _Db extends Table
{
$_v = $nodeClass::load( $_v );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException $e )
{
continue;
}
@@ -306,7 +323,7 @@ class _Db extends Table
$selects = $this->selects;
$isOtherJoin = false;
if ( \count( $this->joins ) )
if ( count( $this->joins ) )
{
foreach( $this->joins as $join )
{
@@ -315,7 +332,7 @@ class _Db extends Table
$selects[] = $join['select'];
}
if ( isset( $join['type'] ) and \mb_strtolower( $join['type'] ) != 'left' )
if ( isset( $join['type'] ) and mb_strtolower( $join['type'] ) != 'left' )
{
/* Inner join or straight join which will affect the query results */
$isOtherJoin = true;
@@ -325,13 +342,15 @@ class _Db extends Table
/* Count results (for pagination) */
$count = $this->db->select( 'count(*)', $this->table, $where, NULL, NULL, $this->groupBy );
/* Add the joins if we have a where statement and all joins are left for the count */
if ( ( $isOtherJoin or \count( $where ) ) and \count( $this->joins ) )
if ( count( $this->joins ) )
{
foreach( $this->joins as $join )
/* Add the joins if we have a where statement and all joins are left for the count */
if ( ( $isOtherJoin or count( $where ) ) and count( $this->joins ) )
{
$count->join( $join['from'], ( isset( $join['where'] ) ? $join['where'] : null ), ( isset( $join['type'] ) ) ? $join['type'] : 'LEFT' );
foreach ( $this->joins as $join )
{
$count->join( $join['from'], ( $join['where'] ?? null ), ( isset( $join['type'] ) ) ? $join['type'] : 'LEFT' );
}
}
}
@@ -340,13 +359,13 @@ class _Db extends Table
$selectPrefix = ( $this->groupBy ) ? '' : $this->table . '.*, ';
/* Now get column headers */
$query = $this->db->select( ( \count( $selects ) ) ? $selectPrefix . implode( ', ', $selects ) : '*', $this->table, NULL, NULL, array( 0, 1 ), $this->groupBy );
$query = $this->db->select( ( count( $selects ) ) ? $selectPrefix . implode( ', ', $selects ) : '*', $this->table, NULL, NULL, array( 0, 1 ), $this->groupBy );
if ( \count( $this->joins ) )
if ( count( $this->joins ) )
{
foreach( $this->joins as $join )
{
$query->join( $join['from'], ( isset( $join['where'] ) ? $join['where'] : null ), ( isset( $join['type'] ) ) ? $join['type'] : 'LEFT' );
$query->join( $join['from'], ( $join['where'] ?? null ), ( isset( $join['type'] ) ) ? $join['type'] : 'LEFT' );
}
}
@@ -354,7 +373,7 @@ class _Db extends Table
{
$results = $query->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$results = array();
}
@@ -393,12 +412,12 @@ class _Db extends Table
}
/* Are we downloading? Bypass Table Limit */
$limit = \IPS\Request::i()->download ? $count : $this->limit;
$limit = Request::i()->download ? $count : $this->limit;
/* Run query */
$rows = array();
$select = $this->db->select(
( \count( $selects ) ) ? $selectPrefix . implode( ', ', $selects ) : '*',
( count( $selects ) ) ? $selectPrefix . implode( ', ', $selects ) : '*',
$this->table,
$where,
$orderBy,
@@ -411,7 +430,7 @@ class _Db extends Table
$select->forceIndex( $this->index );
}
if ( \count( $this->joins ) )
if ( count( $this->joins ) )
{
foreach( $this->joins as $join )
{
@@ -432,14 +451,14 @@ class _Db extends Table
$row = array();
foreach ( $this->include as $k )
{
$row[ $k ] = isset( $_row[ $k ] ) ? $_row[ $k ] : NULL;
$row[ $k ] = $_row[$k] ?? NULL;
}
if( !empty( $advancedSearchValues ) AND !isset( \IPS\Request::i()->noColumn ) )
if( !empty( $advancedSearchValues ) AND !isset( Request::i()->noColumn ) )
{
foreach ( $advancedSearchValues as $k => $v )
{
$row[ $k ] = isset( $_row[ $k ] ) ? $_row[ $k ] : NULL;
$row[ $k ] = $_row[$k] ?? NULL;
}
}
}
@@ -459,7 +478,7 @@ class _Db extends Table
}
/* Are we including this one? */
if( ( ( $this->include !== NULL and !\in_array( $k, $this->include ) ) or ( $this->exclude !== NULL and \in_array( $k, $this->exclude ) ) ) and !array_key_exists( $k, $advancedSearchValues ) )
if( ( ( $this->include !== NULL and !in_array( $k, $this->include ) ) or ( $this->exclude !== NULL and in_array( $k, $this->exclude ) ) ) and !array_key_exists( $k, $advancedSearchValues ) )
{
unset( $row[ $k ] );
continue;
@@ -518,17 +537,17 @@ class _Db extends Table
/**
* User set sortBy is suitable for an SQL sort operation
* @param array $count Result of count(*) query with field names included
* @param array $count Result of count(*) query with field names included
* @return boolean
*/
protected function _isSqlSort( $count )
protected function _isSqlSort( array $count ): bool
{
if ( !$this->sortBy )
{
return false;
}
if( !\is_array( $count ) )
if( !is_array( $count ) )
{
$count = array( $count );
}
@@ -572,7 +591,7 @@ class _Db extends Table
*
* @return array
*/
public function customActions()
public function customActions(): array
{
return array();
}