dataQuery->getQueryParam('Foreign.Class'); } /** * Gets the field name which holds the related object class. */ public function getForeignClassKey(): string { return $this->classForeignKey; } /** * Create a new PolymorphicHasManyList relation list. * * @param string $dataClass The class of the DataObjects that this will list. * @param string $foreignField The name of the composite foreign relation field. Used * to generate the ID and Class foreign keys. * @param string $foreignClass Name of the class filter this relation is filtered against */ public function __construct($dataClass, $foreignField, $foreignClass) { // Set both id foreign key (as in HasManyList) and the class foreign key parent::__construct($dataClass, "{$foreignField}ID"); $this->classForeignKey = "{$foreignField}Class"; // Ensure underlying DataQuery globally references the class filter $this->dataQuery->setQueryParam('Foreign.Class', $foreignClass); // For queries with multiple foreign IDs (such as that generated by // DataList::relation) the filter must be generalised to filter by subclasses $classNames = Convert::raw2sql(ClassInfo::subclassesFor($foreignClass)); $this->dataQuery->where(sprintf( "\"{$this->classForeignKey}\" IN ('%s')", implode("', '", $classNames) )); } /** * Adds the item to this relation. * * It does so by setting the relationFilters. * * @param DataObject|int $item The DataObject to be added, or its ID */ public function add($item) { if (is_numeric($item)) { $item = DataObject::get_by_id($this->dataClass, $item); } elseif (!($item instanceof $this->dataClass)) { throw new InvalidArgumentException( "PolymorphicHasManyList::add() expecting a $this->dataClass object, or ID value" ); } $foreignID = $this->getForeignID(); // Validate foreignID if (!$foreignID) { user_error( "PolymorphicHasManyList::add() can't be called until a foreign ID is set", E_USER_WARNING ); return; } if (is_array($foreignID)) { user_error( "PolymorphicHasManyList::add() can't be called on a list linked to multiple foreign IDs", E_USER_WARNING ); return; } $foreignKey = $this->foreignKey; $classForeignKey = $this->classForeignKey; $item->$foreignKey = $foreignID; $item->$classForeignKey = $this->getForeignClass(); $item->write(); } /** * Remove an item from this relation. * Doesn't actually remove the item, it just clears the foreign key value. * * @param DataObject $item The DataObject to be removed * @todo Maybe we should delete the object instead? */ public function remove($item) { if (!($item instanceof $this->dataClass)) { throw new InvalidArgumentException( "HasManyList::remove() expecting a $this->dataClass object, or ID" ); } // Don't remove item with unrelated class key $foreignClass = $this->getForeignClass(); $classNames = ClassInfo::subclassesFor($foreignClass); $classForeignKey = $this->classForeignKey; $classValueLower = strtolower($item->$classForeignKey ?? ''); if (!array_key_exists($classValueLower, $classNames ?? [])) { return; } // Don't remove item which doesn't belong to this list $foreignID = $this->getForeignID(); $foreignKey = $this->foreignKey; if (empty($foreignID) || $foreignID == $item->$foreignKey || (is_array($foreignID) && in_array($item->$foreignKey, $foreignID ?? [])) ) { $item->$foreignKey = null; $item->$classForeignKey = null; $item->write(); } } }