Files
IPB/system/Node/NodeGroup.php
T
2025-12-19 19:04:57 -08:00

220 lines
5.2 KiB
PHP

<?php
/**
* @brief Group
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage
* @since 5/5/2025
*/
namespace IPS\Node;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Data\Store;
use IPS\Db;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Node;
use IPS\Helpers\Form\Translatable;
use IPS\Lang;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use OutOfRangeException;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
class NodeGroup extends Model
{
/**
* @brief [ActiveRecord] Multiton Store
*/
protected static array $multitons;
/**
* @brief [ActiveRecord] Database Table
*/
public static ?string $databaseTable = 'core_node_groups';
/**
* @brief [ActiveRecord] Database Prefix
*/
public static string $databasePrefix = 'group_';
/**
* @brief [Node] Node Title
*/
public static string $nodeTitle = 'node_groups';
/**
* @brief [Node] Title prefix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}" as the key
*/
public static ?string $titleLangPrefix = 'core_node_group_';
/**
* @brief [ActiveRecord] Caches
* @note Defined cache keys will be cleared automatically as needed
*/
protected array $caches = array( 'nodeGroups' );
/**
* @brief Bitwise values for members_bitoptions field
*/
public static array $bitOptions = array(
'bitoptions' => array(
'bitoptions' => array(
)
)
);
/**
* [Node] Add/Edit Form
*
* @param Form $form The form
* @return void
*/
public function form( Form &$form ) : void
{
$form->add( new Translatable( 'node_group_name', NULL, TRUE, array( 'app' => 'core', 'key' => ( $this->id ? static::$titleLangPrefix . $this->id : NULL ) ) ) );
/* The node class should already be set at this point, but let's be sane */
if( $this->class )
{
$field = new Node( 'node_group_nodes', iterator_to_array( $this->nodes() ), false, [
'class' => $this->class,
'multiple' => true
] );
$nodeClass = $this->class;
$field->label = Member::loggedIn()->language()->addToStack( $nodeClass::$nodeTitle );
$form->add( $field );
}
parent::form( $form );
}
/**
* [Node] Format form values from add/edit form for save
*
* @param array $values Values from the form
* @return array
*/
public function formatFormValues( array $values ): array
{
if( !$this->id )
{
$this->save();
}
Lang::saveCustom( 'core', static::$titleLangPrefix . $this->id, $values['node_group_name'] );
unset( $values['node_group_name'] );
if( !empty( $values['node_group_nodes'] ) )
{
$current = iterator_to_array(
Db::i()->select( 'node_id', 'core_node_groups_nodes', [ 'group_id=?', $this->id ] )
);
$usedIds = [];
foreach( $values['node_group_nodes'] as $node )
{
$usedIds[] = $node->_id;
if( !in_array( $node->_id, $current ) )
{
Db::i()->insert( 'core_node_groups_nodes', [ 'group_id' => $this->id, 'node_id' => $node->id ] );
}
}
/* Anything to delete? */
$toDelete = array_diff( $current, $usedIds );
if( count( $toDelete ) )
{
Db::i()->delete( 'core_node_groups_nodes', [
[ 'group_id=?', $this->id ],
[ Db::i()->in( 'node_id', $toDelete ) ]
]);
}
}
unset( $values['node_group_nodes'] );
return $values;
}
/**
* Return all nodes that are part of this group
*
* @return ActiveRecordIterator
*/
public function nodes() : ActiveRecordIterator
{
$nodeClass = $this->class;
return new ActiveRecordIterator(
Db::i()->select( '*', $nodeClass::$databaseTable, Db::i()->in( $nodeClass::$databasePrefix . $nodeClass::$databaseColumnId, $this->nodeIds() ) ),
$nodeClass
);
}
/**
* Easier way to get the node IDs from the cache
*
* @return array
*/
public function nodeIds() : array
{
return static::getStore()[ $this->class ][ $this->id ]['nodes'] ?? [];
}
/**
* Attempt to load cached data
*
* @note This should be overridden in your class if you enable $loadFromCache
* @see ActiveRecord::$loadFromCache
* @return array
*/
public static function getStore(): array
{
try
{
$cache = Store::i()->nodeGroups;
}
catch( OutOfRangeException )
{
$cache = [];
foreach( Db::i()->select( '*', static::$databaseTable ) as $row )
{
if( !isset( $cache[ $row['group_class'] ] ) )
{
$cache[ $row['group_class'] ] = [];
}
$row['nodes'] = iterator_to_array(
Db::i()->select( 'node_id', 'core_node_groups_nodes', [ 'group_id=?', $row['group_id'] ] )
);
$cache[ $row['group_class'] ][ $row['group_id'] ] = $row;
}
Store::i()->nodeGroups = $cache;
}
return $cache;
}
/**
* [ActiveRecord] Delete Record
*
* @return void
*/
public function delete() : void
{
parent::delete();
Db::i()->delete( 'core_node_groups_nodes', [ 'group_id=?', $this->id ] );
}
}