Ответ 1
Я потратил много времени на это и без принципиальной модификации ManyManyList
(так как он не раскрывает необходимые крючки через систему расширения), выбора не так много.
Я человек десертного типа, как мы можем это сделать?
Мое единственное предложение выполнить этот подвиг - это, по существу, мостовой объект "много ко многим" (т.е. отдельный объект, соединяющий Page
и Image
) через $has_many
, хотя он по-прежнему требует довольно небольшой модификации.
Это частично обсуждается на форуме, где решение о подрыве фактической взаимосвязи, сохраняя элементы с версией в сравнении с реальным объектом, а не в соединение таблица. Это будет работать, но я думаю, что мы все еще можем сделать лучше.
Я лично склоняюсь к привязке версии отношения к Page
, и мое частное решение ниже описывает это. Читайте ниже складки, чтобы узнать больше об этом как об обновлении до ManyManyList
.
Что-то вроде этого - это начало:
class PageImageVersion extends DataObject
{
private static $db = array(
'Version' => 'Int'
);
private static $has_one = array(
'Page' => 'Page',
'Image' => 'Image'
);
}
Это содержит наши двухсторонние отношения, плюс мы сохранили наш номер версии. Вы хотите указать функцию getCMSFields
, чтобы добавить нужные нужные поля, чтобы вы могли связать их с существующим изображением или загрузить новый. Я избегаю этого, так как он должен быть относительно прямым, по сравнению с фактической частью обработки.
Теперь мы имеем has_many
на Page
так:
private static $has_many = array(
'Images' => 'PageImageVersion'
);
В моих тестах я также добавил расширение для Image
, добавляя на него сопоставление $has_many
так же:
class ImageExtension extends DataExtension
{
private static $has_many = array(
'Pages' => 'PageImageVersion'
);
}
Честно говоря, не уверен, что это необходимо, кроме добавления
Pages
функции на сторонеImage
отношения. Насколько я могу судить, это не имеет особого значения для этого конкретного использования.
К сожалению, из-за этого способа управления версиями мы не можем использовать стандартный способ вызова Images
, нам нужно быть немного творческим. Что-то вроде этого:
public function getVersionedImages($Version = null)
{
if ($Version == null)
{
$Version = $this->Version;
}
else if ($Version < 0)
{
$Version = max($this->Version - $Version, 1);
}
return $this->Images()->filter(array('Version' => $Version));
}
Когда вы вызываете getVersionedImages()
, он вернет все изображения, у которых Version
установлен на нем, совпадающем с версией текущей страницы. Также поддерживает получение предыдущих версий с помощью getVersionedImages(-1)
для последней версии или даже получение изображений для определенной версии страницы путем передачи любого номера позиции.
Хорошо, пока все хорошо. Теперь нам нужно убедиться, что каждая страница пишет, что мы получаем дублированный список изображений для этой новой версии страницы.
С помощью функции onAfterWrite
на Page
мы можем сделать это:
public function onAfterWrite()
{
$lastVersionImages = $this->getVersionedImages(-1);
foreach ($lastVersionImages as $image)
{
$duplicate = $image->duplicate(false);
$duplicate->Version = $this->Version;
$duplicate->write();
}
}
Для тех, кто играет дома, это то, где кое-что немного напоминает о том, как это повлияет на восстановление предыдущих версий
Page
.
Поскольку мы будем редактировать это в GridField
, нам нужно будет сделать несколько вещей. Сначала убедитесь, что наш код может обрабатывать функцию Add New
.
Моя идея onAfterWrite
в объекте PageImageVersion
:
public function onAfterWrite()
{
//Make sure the version is actually saved
if ($this->Version == 0)
{
$this->Version = $this->Page()->Version;
$this->write();
}
}
Чтобы отобразить отображаемые вами элементы в GridField
, вы должны настроить его так:
$gridFieldConfig = GridFieldConfig_RecordEditor::create();
$gridField = new GridField("Images", "Images", $this->getVersionedImages(), $gridFieldConfig);
$fields->addFieldToTab("Root.Images", $gridField);
Вам может потребоваться ссылка на изображения непосредственно из GridField
через GridFieldConfig_RelationEditor
, но это происходит, когда все становится кислым.
Время для овощей...
Одна из больших трудностей - GridField
, как для связывания, так и для развязывания этих объектов. Использование стандартного GridFieldDeleteAction
будет напрямую обновлять отношения без правильной версии.
Вам нужно будет расширить GridFieldDeleteAction
и переопределить handleAction
для записи вашего объекта Page
(для запуска другой версии), дублировать каждую версию нашего объекта с версией изображения для последней версии, в то же время пропуская ту, которую вы не хотите в новой версии.
Я признаю, что этот последний бит - это только догадки. Из моего понимания и отладки, он должен работать, но просто есть много попыток, чтобы понять это.
Ваше расширение GridFieldDeleteAction
затем должно быть добавлено к вашему конкретному GridField
.
Это будет ваш последний шаг от решения этого решения. После того, как вы добавите, удалите, дублируете, обновите часть версии, действительно нужно просто использовать getVersionedImages()
для получения правильных изображений.
Заключение
Избегайте.. Я понимаю, почему вы хотите это сделать, но я действительно не вижу чистого способа справиться с этим без изменения размера приличного размера до того, как отношения many_many
обрабатываются в SilverStripe.
Но я действительно хочу, чтобы это было как ManyManyList
!
Изменения, которые я вижу для ManyManyList
, имеют 3-позиционный ключ (внешний ключ, локальный ключ, ключ версии) и обновлены различные способы добавления/удаления/выборки и т.д.
Если в функциях add
и remove
есть крючки, вы можете прокрасться в функциональность как расширение (через систему расширения Silverstripe) и добавить необходимые данные в дополнительные поля, которые many_many
отношения позволяют.
Пока я мог это сделать, расширив ManyManyList
напрямую, а затем заставив ManyManyList
заменить мой пользовательский класс через Object::useCustomClass
, это было бы еще более беспорядочным решением.
Для меня просто слишком сложно/сложно дать полный ответ для чистого решения ManyManyList
на этом этапе (хотя я могу вернуться к этому позже и дать ему шанс).
Отказ от ответственности: я не являюсь разработчиком Silverstripe Core, может быть более аккуратное решение для всего этого, но я просто не вижу, как это сделать.