Наверх

Часть 5. Создание и удаление объектов

Мы уже умеем получать данные объектов, можем устанавливать новые значения полей, знаем, что существуют связи между объектами. Пора научиться создавать объекты и удалять их.

Для того, чтобы создать объект мы будем пользоваться методом newObject()
newObject() — создает новый объект указанного класса.
Но не забывайте сохранять объекты — здесь как с файлами — сначала создаем новый файл, потом наполняем его содержимым и сохраняем на диск.

Давайте, поработаем с чем-то еще, кроме ресурсов. Например, создадим новую категорию элементов:
$category = $modx->newObject('modCategory');
// название категорий хранится в поле category
$category->set('category','helloWorldCategory');
$category->save();

Выполните этот код и найдите только что созданную категорию во вкладке «Элементы». Если нашли, то давайте посмотрим, как создаются связи между объектами. Обычно, если у объекта есть дочерние или родительские объекты (как например, ресурс — у него могут быть дочерние ресурсы, и может быть родительский ресурс), это значит, что два объекта связаны друг с другом. То есть, мы можем получить объект родительского ресурса так:
$child = $modx->getObject('modResource', 43);
// опять смотрим правильный алиас связи (в этот раз - "Parent")
$parent = $child->getOne('Parent');
return $parent->get('pagetitle');
Значит, если мы хотим создать чанк «в нашей категории», мы должны сначала создать чанк как объект, а потом создать связь между чанком и категорией. Для создания связей между объектами (в случае, если тип связей уже известен MODX'у) используются методы addOne и addMany.
addOne() — создает связь между двумя объектами. Используется, когда связь «Один — к одному»
addMany() — создает связи между объектом и каждым из элементов массива. Используется, когда связь «Один — ко многим»
Немножко непонятно написал про addMany. Суть в том, что addMany прежде чем создать связь проходит по массиву. В итоге создается связь нашего объекта с каждым из объектов-элементов массива.

Но тут лучше привести пример. Сейчас создадим один чанк и свяжем его с нашей категорией. А потом сделаем то же самое с целой пачкой чанков)))
$where = array('category' => 'helloWorldCategory');
$category = $modx->getObject('modCategory', $where);
$chunk = $modx->newObject('modChunk');
$chunk->set('name','helloWorldChunk');
$chunk->addOne($category);
$chunk->save();
Чанк может находится только в одной категории, а значит, связь «один — к одному» и мы используем addOne.

Следующий пример — давайте создадим сразу несколько чанков в цикле. Тут есть два варианта действий: либо для каждого нового чанка вызывать addOne, либо помещать все чанки в массив и потом один раз для категории вызывать addMany. Первый вариант:
$where = array('category' => 'helloWorldCategory');
$category = $modx->getObject('modCategory', $where);
for ($i=1; $i<=3; $i++) {
   $chunk = $modx->newObject('modChunk');
   $chunk->set('name', 'helloWorldChunk.'.$i);
   $chunk->addOne($category);
   $chunk->save();
}
Второй вариант:
$where = array('category' => 'helloWorldCategory');
$category = $modx->getObject('modCategory', $where);
$chunks = array();
for ($i=1; $i<=3; $i++) {
   $chunk = $modx->newObject('modChunk');
   $chunk->set('name', 'helloWorldChunk.'.$i);
   $chunks[] = $chunk;
}
$category->addMany($chunks);
$category->save();
Как видите, во втором варианте мы работаем с массивом объектов. В одной категории может быть много чанков, значит связь «Один — ко многим» (что подтверждается здесь), и мы используем addMany.

Как узнать, какой из вариантов предпочтительнее? Обратите внимание на вызовы метода save() — в первом случае save() вызывается для каждого из чанков, а во втором — только однажды, в конце кода и вызывается он для категории. Как же так, спросите вы — ведь каждый объект должен быть сохранён? Да, совершенно верно, но в методе save() происходит сохранение не только самого объекта, но и объектов, связанных с ним. Поэтому нам во втором случае не нужно каждый раз для каждого чанка прописывать save(). И именно поэтому второй вариант предпочтительнее, так как мы не вызываем один и тот же метод не по нескольку раз, а только однажды — и он пачкой обрабатывает все объекты.

Теперь давайте, подчистим за собой и удалим наши чанки и категорию. Для удаления объекта существует метод remove()
remove() — удаляет объект и все объекты, связанные с ним связью «composite», если у объекта в описаниях связи указано «owner => local»
Как я говорил, существует два вида связей — composite и agregate. Так вот, есть у нас объект, например, категория. И есть у нас несколько дочерних объектов, например, есть одна подкатегория и два чанка. Если посмотрим в описание объекта modCategory то увидим, что есть такая запись:
[composites]
        [Children]
            [class] => modCategory
            [local] => id
            [foreign] => parent
            [cardinality] => many
            [owner] => local
и такая запись
[aggregates]
        [Chunks]
            [class] => modChunk
            [key] => id
            [local] => id
            [foreign] => category
            [cardinality] => many
            [owner] => local
Так вот, если вы выполните метод remove() по отношению к категории, то все дочерние категории будут удалены вместе с ней (связь composite, [owner] => local). Но чанки (как и другие элементы) удалены не будут (хоть и [owner] => local, но связь-то aggregate).

Поэтому чтобы удалить все чанки в како-то категории нужно сначала их все удалить, а потом удалять саму категорию:
$where = array('category' => 'helloWorldCategory');
$category = $modx->getObject('modCategory', $where);
$chunks = $category->getMany('Chunks');
foreach ($chunks as $chunk) {
    $chunk->remove();
}
$category->remove();
После выполнения этого кода консоль выведет вам информацию о том, что объекты удалены. Не пугайтесь — это не ошибки, а просто лог удаленных объектов.
Задание. Напишите сниппет, который создает ресурс в каком-либо разделе. Значения полей будут передаваться в сниппет как параметры:
[[createResource? &pagetitle=`Заголовок` &parent=`12` ... ]]
Поля parent, createdby, template не заполняйте с помощью set(), а создавайте связь с нужными объектами.
Оригинал статьи community.modx-cms.ru/blog/modx-xpdo/10275.html


30 комментариев

  1. Алексей 18 февраля 2015, 14:06 # 0
    Подскажите пожалуйста — можно ли программно изменить base_url?
    1. Илья Уткин 18 февраля 2015, 14:07 # 0
      Можно:
      $modx->setOption('base_url', 'http://new.base.url/');
      1. Александр 11 июня 2015, 20:17 # 0
        Илья подскажите данная статья не применима к ModX Evo?
        1. Илья Уткин 12 июня 2015, 08:14 # 0
          Неа, не применима. Evolution не использует xPDO
          1. Александр 12 июня 2015, 08:23 # 0
            А вообще есть возможность у эво массово страницы создавать?
            1. Илья Уткин 12 июня 2015, 08:25 # 0
              Не знаю. Я с ЕВО не работал. Спросите на modx.im/
    2. Александр 12 июня 2015, 08:31 # 0
      Спасибо
      1. Леви Ким 27 марта 2016, 10:58 # 0
        Добрый день!
        подскажите, если я создаю пользователя, могу ли я задать ему ID? из обьектной модели я понял что у меня нет опции добраться до айди и он генерируется сам.
        Просто, у меня есть импорт пользователей, и глупо иметь два, по сути дублирующихся, поля.
        пробовал так, но, как я предполагал, это не сработало:
        $user = $modx->newObject('modUser');
        $user->set('id', $userId);
        $user->set('username', $userId);
        $user->save();
        Заранее спасибо за совет
        1. Илья Уткин 28 марта 2016, 08:24 # 0
          Да, похоже, к id доступа нет, нужно хранить id пользователя из импорта в каком-то дополнительном поле…
          1. Леви Ким 28 марта 2016, 08:26 # 0
            а что такое [internalKey] в modUserProfile? потому что в описании он есть, а добраться до него тоже не получается
            1. Илья Уткин 28 марта 2016, 08:27 # 0
              А это ссылка на ID объекта modUser. Через это поле объекты связываются друг с другом
        2. Aнтон К 05 июля 2016, 13:14 # 0
          Большое спасибо за статьи с практическими задачами.

          Пока не совсем разобрался как создавать Плагины, какие особенности, где брать информацию о событиях и как их обрабатывать. Может поделитесь какой-нибудь ссылкой, где это описывается, или напишите еще одну статью)

          И не получается создать связь с родителем, что-то делаю не так.

          Буду благодарен за ответ)
          1. Илья Уткин 05 июля 2016, 13:20 # +1
            Вот немного информации о плагинах: https://ilyaut.ru/xpdo/xpdo-for-dummies-part-3-write-a-plugin/, кроме того, список событий есть в админке — при создании плагина есть вкладка «События».

            Чтобы понять, какое событие за что отвечает, можно поискать его в документации https://rtfm.modx.com/revolution/2.x/developing-in-modx/basic-development/plugins/system-events/ или прямо в исходном коде MODX на GitHub.
            1. Aнтон К 05 июля 2016, 13:25(Комментарий был изменён) # 0
              Спасибо) надо почаще заглядывать в оф.документацию))
          2. Владимир 12 мая 2017, 19:50 # 0
            Илья! спасибо Вам огромное за статью. Но не могу выполнить задание.
            почему-то никак не работает следующий код
            $rParent = $modx->getObject('modResource',6);
            
            $res = $modx->newObject('modResource');
            $res->set('pagetitle','sfds');
            $res->addOne($rParent);
            $res->save();
            Выдает ошибку связи.
            1. man 14 мая 2017, 00:15(Комментарий был изменён) # 0
              Вместо:
              $res->addOne($rParent);
              надо:
              $res->set('parent', $parent);
              1. Владимир 14 мая 2017, 18:45 # 0
                Да, так-то оно так! но я хотел создать именно связь с родителем. Кроме того, Илья в задании просил не использовать set
                1. man 15 мая 2017, 10:35 # 0
                  ааа) ну тогда сорян, я в задание не глянул)
              2. Илья Уткин 16 мая 2017, 09:20 # 0
                Действительно) Дело в том, что у ресурса может быть как родитель, так и дочерние ресурсы. При таком варианте появляется неоднозначность. Чтобы её избежать, нужно явно указать тип (алиас) связи вторым параметром:
                $res->addOne($rParent, 'Parent');
                1. Владимир 16 мая 2017, 18:35 # 0
                  От души! Сработал приёмчик)
              3. Андрей 06 июня 2017, 16:47 # 0
                Илья, а как удалить значение определенной опции у товара minishop?
                Пишу такой код:
                <?php
                $rid = 6;
                /** @var $product msProduct */
                $product = $modx->getObject('msProduct', $rid);
                $options = $product->loadData()->get('options');
                foreach ($options as $k => $v) {
                    if (is_array($v)) {
                        $v = array_unique($v);
                    }
                
                }
                foreach ($options['color'] as $item ) { 
                   $item->remove();
                }
                $product->save();
                В результате получаю ошибку: «Fatal error: Call to a member function remove() on array in /core/components/console/processors/exec.class.php(24): eval()'d code on line 19»
                1. Илья Уткин 15 июня 2017, 15:53 # 0
                  Я думаю, дело в том, что опции хранятся в виде массива, а не в виде объектов. У массивов нет метода remove. Должно быть как-то так:

                  foreach ($options['color'] as $key => $item ) { 
                     unset($options['color'][$key]);
                  }
                2. Михаил Дегтярев 29 марта 2018, 15:58 # 0
                  Подскажите пожалуйста! Как проверить какому пользователю принадлежит ресурс?
                  У меня есть задача что бы пользователи с фронтенда могли удалять или снимать с публикации созданные ими ресурсу.
                  1. Михаил Дегтярев 02 апреля 2018, 08:42 # 0
                    ААА сам разобрался)))
                    пока тут дождёшься)))
                    <a href="profil/?id=143&user=1&pub=unpub">Снять с публикации</a>
                    
                    <?php
                    $res = $modx->getObject('modResource',$id);
                    $createdby = $res->get('createdby');
                    
                    $user = $modx->user->getOne('Profile');
                    $profile = $user->get('id');
                    
                    if($createdby == $profile){
                        if($_GET['user'] == $profile ) {
                            $unid = $_GET['id'];
                            $resource = $modx->getObject('modResource', $unid );
                            $resource->set('published',1);
                            $resource->save();
                        }
                    }
                    1. Максим 11 июля 2018, 09:38 # 0
                      Здравствуйте, подскажите можно ли как то получить id только что созданного объекта?
                      1. Илья Уткин 12 июля 2018, 10:05 # 0
                        Да, после save у объекта появляется ID и можно получить его с помощью метода get
                        $category->save();
                        $id = $category->get('id');
                        1. Рушан 10 сентября 2020, 17:03(Комментарий был изменён) # 0
                          Здравствуйте!
                          А можно этот код переделать на снятие с публикации ресурса miniShop2?
                          При нажатии кнопки.
                          <?php
                          $res = $modx->getObject('modResource',$id);
                          $createdby = $res->get('createdby');
                          
                          $user = $modx->user->getOne('Profile');
                          $profile = $user->get('id');
                          
                          if($createdby == $profile){
                              if($_GET['user'] == $profile ) {
                                  $unid = $_GET['id'];
                                  $resource = $modx->getObject('modResource', $unid );
                                  $resource->set('published',1);
                                  $resource->save();
                              }
                          }
                          1. Илья Уткин 11 сентября 2020, 13:01 # 0
                            $resource->set('published',0);
                      2. Алексей 29 июня 2021, 10:04 # 0
                        Здравствуйте! Если мне необходимо перенести некий скрипт php, не сниппет, а просто вспомогательную библиотеку для моего модуля, как быть в этом случае, и как задать ему путь в файловой системе CMS. Спасибо!
                        1. Илья Уткин 30 июня 2021, 07:58 # 0
                          Здравствуйте. Тут всё зависит от модуля, «волшебной» кнопки нет, просто так сторонний скрипт интегрировать не получится.

                        Авторизация

                        через сервис Loginza:


                        Шаблоны MODX

                        1 2 Дальше »

                        Объектная
                        модель
                        MODX