Для начала нужно продумать модель ваших объектов — какие поля вам понадобятся. Я возьму для примера формирование экзаменационных листов на пересдачу в вузе.

Необходимые мне поля:
- №
- ФИО студента
- Группа
- Курс
- Предмет
- Преподаватель
- Дата выдачи
- Оценка
- Кто оформил (пользователь)
В соответствие с этой моделью создаем таблицу в базе и генерируем модель в MODX по инструкции из предыдущего урока. Я не особо уверен, что мне хватит полей и что я не захочу потом добавить еще что-то, так что я добавлю 2-3 дополнительных поля, чтобы использовать их в таком случае.
Моя таблица modx_exam_sheet будет выглядеть вот так:
DROP TABLE IF EXISTS `modx_exam_sheet`; CREATE TABLE IF NOT EXISTS `modx_exam_sheet` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `student` varchar(255) DEFAULT NULL, `group` varchar(255) DEFAULT NULL, `course` int(1) unsigned DEFAULT NULL, `subject` varchar(255) DEFAULT NULL, `examiner` varchar(255) DEFAULT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `score` int(1) unsigned DEFAULT NULL, `created_by` int(10) unsigned DEFAULT NULL, `add1` varchar(255) DEFAULT NULL, `add2` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `date` (`date`,`created_by`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;Соответственно, сгенерированный CMPGenerator'ом пакет называется exam, а новый объект получил класс ExamSheet.
Пробуем выполнить в консоли следующий код:
$sheet = $modx->newObject('ExamSheet');
$sheet->save();После этого проверяем, появилась ли в базе данных новая запись. Если да, значит, все ОК и можно переходить к следующему этапу.
Сразу установим FormIt, Jevix, pdoTools и, если верстка не готова, то и Theme.Bootstrap.
Вывод списка объектов
Создаем сниппет getSheets, который будет выводить общий список (будем использовать pdoTools для вывода):
$path = MODX_CORE_PATH . 'components/pdotools/model/pdotools/';
$pdoFetch = $modx->getService('pdofetch','pdoFetch', $path, $scriptProperties);
$pdoFetch->setConfig($scriptProperties);
return $pdoFetch->run();И вызываем его на странице:[[!pdoPage?
&class=`ExamSheet`
&tpl=`tpl.examSheet`
&element=`getSheets`
]]
[[!+page.nav]]В чанке tpl.examSheet пока напишем просто[[+id]]. [[+student]]Если вы вручную добавите в таблицу строк, то увидите их на странице. Причем, уже с пагинацией.
Добавление объектов
Переходим к добавлению объектов пользователями. Заходим в папку /core/components/exam/processors/ и создаем в ней папку sheet, а в ней уже файл create.class.php:
<?php
class ExamSheetCreateProcessor extends modObjectCreateProcessor {
public $classKey = 'ExamSheet';
public $objectType = 'object';
public function beforeSet() {
if (!$this->modx->user->id) return 'Вам нужно авторизоваться';
$this->setProperty('created_by', $this->modx->user->id);
return true;
}
}
return 'ExamSheetCreateProcessor';и сниппет examSheet:if (!$_POST['sheet']) {
$processor = 'sheet/create';
} else {
$_POST['id'] = $_POST['sheet'];
$modx->setPlaceholder('id', $_POST['id']);
$processor = 'sheet/update';
}
$processorProps = array('processors_path' => $modx->getOption('core_path')
. 'components/exam/processors/');
$response = $modx->runProcessor($processor, $_POST, $processorProps);
if ($response->isError()) {
$hook->addError('process_error', $response->getMessage());
return false;
}
return true;Теперь делаем страничку добавления/редактирования объекта. Для проверки полей будем использовать FormIt (сверстано для Bootstrap):

[[!getSheetData]]
[[!FormIt?
&hooks=`examSheet,redirect`
&redirectTo=`1`
&placeholderPrefix=``
&validate=`student:required,
group:required,
course:required,
subject:required,
examiner:required,
score:required`
]]
<div class="row">
<div class="col-lg-6" style="margin-left: auto; margin-right: auto; float: none;">
[[!+error.process_error:notempty=`<div class="alert alert-dismissable alert-danger">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>Ошибка!</strong> [[!+error.process_error]]
</div>`]]
<div class="well">
<form class="bs-example form-horizontal" action="[[~[[*id]]]]" method="post">
<input type="hidden" value="[[!+id]]" name="sheet">
<fieldset>
<legend>[[*pagetitle]]</legend>
<div class="form-group [[!+error.student:notempty=`has-error`]]">
<label for="student" class="col-lg-4 control-label">Студент</label>
<div class="col-lg-8">
<input type="text" value="[[!+student]]" class="form-control"
id="student" placeholder="Иванов Александр Юрьевич" name="student">
<span class="help-block">[[!+error.student]]</span>
</div>
</div>
<div class="form-group [[!+error.group:notempty=`has-error`]]">
<label for="group" class="col-lg-4 control-label">Группа</label>
<div class="col-lg-8">
<input type="text" value="[[!+group]]" class="form-control"
id="group" placeholder="МО-54" name="group">
<span class="help-block">[[!+error.group]]</span>
</div>
</div>
<div class="form-group [[!+error.course:notempty=`has-error`]]">
<label for="course" class="col-lg-4 control-label">Курс</label>
<div class="col-lg-8">
<input type="text" value="[[!+course]]" class="form-control"
id="course" placeholder="5" name="course">
<span class="help-block">[[!+error.course]]</span>
</div>
</div>
<div class="form-group [[!+error.subject:notempty=`has-error`]]">
<label for="subject" class="col-lg-4 control-label">Предмет</label>
<div class="col-lg-8">
<input type="text" value="[[!+subject]]" class="form-control"
id="subject" placeholder="Статистика" name="subject">
<span class="help-block">[[!+error.subject]]</span>
</div>
</div>
<div class="form-group [[!+error.examiner:notempty=`has-error`]]">
<label for="examiner" class="col-lg-4 control-label">Преподаватель</label>
<div class="col-lg-8">
<input type="text" value="[[!+examiner]]" class="form-control"
id="examiner" placeholder="Ефремов Виталий Борисович" name="examiner">
<span class="help-block">[[!+error.examiner]]</span>
</div>
</div>
<div class="form-group [[!+error.score:notempty=`has-error`]]">
<label for="score" class="col-lg-4 control-label">Оценка</label>
<div class="col-lg-8">
<input type="text" value="[[!+score]]" class="form-control"
id="score" placeholder="4" name="score">
<span class="help-block">[[!+error.score]]</span>
</div>
</div>
<div class="form-group">
<div class="col-lg-8 col-lg-offset-4">
<button type="submit" class="btn btn-primary">Сохранить</button>
<button type="submit" class="btn btn-danger btn-xs"
name="remove" value="remove"
onclick="return confirm('Удалить экзаменационный лист?')">Удалить</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>Вот и все! Вы можете добавлять объекты через фронтенд. Можно создать несколько пользователей — в базе всегда будет сохраняться, кто добавил тот или иной объект.Редактирование объектов
Внимательные заметили в коде вызов сниппета getSheetData. Это уже для редактирования объекта. Его код:
if ($_GET['sheet'] && $sheet = $modx->getObject('ExamSheet', $_GET['sheet'])) {
$modx->setPlaceholders($sheet->toArray());
}Этот сниппет получает данные объекта и набивает их в плейсхолдеры. А эти плейсхолдеры у нас стоят в полях формы. Соответственно, если вы припишете к адресу страницы добавления get-параметр sheet с id-шником нужного объекта, вы получите форму, заполненную данными объекта. А в сниппете examSheet у нас уже заложено разветвление — если указан id объекта, используется процессор update. Осталось его создать — создаем файл /core/components/exam/processors/sheet/update.class.php:<?php
class ExamSheetUpdateProcessor extends modObjectUpdateProcessor {
public $classKey = 'ExamSheet';
public $objectType = 'object';
public function beforeSet() {
if (!$this->modx->user->id) return 'Вам нужно авторизоваться';
if ($this->object->get('created_by') != $this->modx->user->id)
return 'Вы не можете редактировать чужие записи';
return true;
}
}
return 'ExamSheetUpdateProcessor';Процессор проверяет не только авторизацию пользователя, но и принадлежность объекта к текущему пользователю.Все эти проверки добавлены для примера, чтобы вы понимали, где что нужно писать. Вы можете убрать все проверки, тогда даже анонимный пользователь сможет делать с вашими объектами что угодно.
Поправим чанк tpl.examSheet:
<tr>
<td>[[+id]]</td>
<td><a href="[[~4? &sheet=`[[+id]]`]]">[[+student]]</a></td>
<td>[[+group]]</td>
<td>[[+course]]</td>
<td>[[+subject]]</td>
<td>[[+examiner]]</td>
<td>[[+date:strtotime:date=`%d.%m.%Y г.`]]</td>
<td>[[+score]]</td>
</tr>и вызов на странице списка:<a href="[[~4]]" class="btn btn-primary">
<span class="glyphicon glyphicon-plus"></span>
Выдать экзаминационный лист
</a>
<table class="table">
<thead>
<tr>
<th>№</th>
<th>Студент</th>
<th>Группа</th>
<th>Курс</th>
<th>Предмет</th>
<th>Преподаватель</th>
<th>Дата выдачи</th>
<th>Оценка</th>
</tr>
</thead>
<tbody>
[[!pdoPage?
&class=`ExamSheet`
&tpl=`tpl.examSheet`
&element=`getSheets`
&limit=`50`
]]
</tbody>
</table>
[[!+page.nav]]Можете потестировать создание и редактирование (по нажатию на ФИО студента).Удаление объектов
Для добавления функции удаления объектов, правим сниппет ExamSheet:
if (!$_POST['sheet']) {
$processor = 'sheet/create';
} else {
$_POST['id'] = $_POST['sheet'];
$modx->setPlaceholder('id', $_POST['id']);
if ($_POST['remove']) {
$processor = 'sheet/remove';
} else {
$processor = 'sheet/update';
}
}
$processorProps = array('processors_path' => $modx->getOption('core_path')
. 'components/exam/processors/');
$response = $modx->runProcessor($processor, $_POST, $processorProps);
if ($response->isError()) {
$hook->addError('process_error', $response->getMessage());
return false;
}
return true;и создаем файл-процессор /core/components/exam/processors/sheet/remove.class.php:<?php
class ExamSheetRemoveProcessor extends modObjectRemoveProcessor {
public $classKey = 'ExamSheet';
public $objectType = 'object';
public function beforeRemove() {
if (!$this->modx->user->id) return 'Вам нужно авторизоваться';
if ($this->object->get('created_by') != $this->modx->user->id)
return 'Вы не можете удалять чужие записи';
return true;
}
}
return 'ExamSheetRemoveProcessor';В следующий раз планирую сделать фильтрацию, сортировку, поиск записей. А на сегодня материала и так много.
Объектная
Заранее спасибо за ответ!
[[!pdoPage? &class=`ExamSheet` &tpl=`tpl.examSheet` &element=`getSheets` &where=`{"created_by":[[!+modx.user.id]]}` ]] [[!+page.nav]]Доступны все стандартные выборки))Прописан код выше(),
но в данном примере можно прочитать не свои поля при редактировании так:
/index.php?id=4&sheet=(вот тут подставить не свой ID)
if ($_GET['sheet'] && $sheet = $modx->getObject('ExamSheet', $_GET['sheet'])) { if ($sheet->get('created_by') == $modx->user->id) { $modx->setPlaceholders($sheet->toArray()); } }&where=`{"created_by":[[!+modx.user.id]]}`прописать свой параметр, переданный через ссылку, скажем для фильтра по преподавателям&where=`{"examiner":[[examiner_id]]}`А как можно этот параметр из ссылки забрать?
// /path/to/script?examiner=150 &where=`{"examiner":[[!examiner_id]]}`examiner_id
&where=`{"created_by":"Коля"},{"created_by":"Вася"}`&where=`{"created_by:IN":[15,63,122]}` &where=`{"name:IN":["Коля","Вася"]}`Не происходит редирект на следующую страницы ( а возвращает туже страницу, с которой совершался переход по меню пагинации).
Причем ссылка в строке браузера меняется с */index.php?id=3&page=3 на */index.php?id=3&id=3.
Возможно не установлен какой-то из плагинов? по данной проблеме не нашел совпадений в поиске.
[[!pdoPage? &class=`ExamSheet` &tpl=`tpl.examSheet` &element=`getSheets` &where=`{"created_by":[[!+modx.user.id]],"published":1}` ]] [[!+page.nav]]По этому сниппету
Подскажи, пожалуйста, можно ли получить кол-во строк в которых «published»:1?
[[!pdoPage? &class=`ExamSheet` &tpl=`tpl.examSheet` &element=`getSheets` &where=`{"created_by":[[!+modx.user.id]],"published":1}` ]] [[!+page.nav]] [[+total]]Подскажи плиз как можно сделать вот такую схему:
Заходим на страницу где таблица, не нажимаем редактировать.
Напротив каждой строки есть чекбокс.
Поставили галочку.
Нажали сохранить.
И изменения пришли в базу.
Если есть галочка, то published = 1
Если нет то published = 0
Спасибо за урок!
Отличные статьи пишешь, просто и доступно!
Совсем недавно читаю Вас, но все написано предельно доходчиво. Вопрос такой: Вы сказали добавите 2-3 дополнительных поля на всякий случай. А если и этих трех полей не хватит, то будет ли потом проблематично добавить еще поля? Вы их 2-3 добавили из за удобства или же из-за технической сложности добавления новых полей в будущем? Спасибо!
То есть нужно удалить пакет в CMP generator и удалить папки компонента в core и assets, а потом заново его создать?
В CMPGenerator пакет сгенерировал, он появился в components.
В консоли
$modx->addExtensionPackage('statistic', '[[++core_path]]components/statistic/model/');сделал, соответствующий ключик со значением в настройках появился.А когда в консоли пытаюсь
<?php $item = $modx->newObject('StatisticSheet'); $item->save();сделать — появляется гифка ожидания и ничего не происходит :(В журнале — чисто. Где мог ошибиться?
привожу пример кода:
<?php if (!$_POST['jobsstatus']) { $processor = 'jobsstatus/create'; } else { $_POST['id'] = $_POST['jobsstatus']; $modx->setPlaceholder('id', $_POST['id']); if ($_POST['remove']) { $processor = 'jobsstatus/remove'; } else { $processor = 'jobsstatus/update'; } } $processorProps = array('processors_path' => $modx->getOption('core_path') . 'components/Jobs/processors/'); $response = $modx->runProcessor($processor, $_POST, $processorProps); if ($response->isError()) { $hook->addError('process_error', $response->getMessage()); return false; } return true;создал классы апдейта и удаления, что я делаю не так?<?php if ($_GET['jobsstatus'] && $jobsstatus = $modx->getObject('JobsStatus', $_GET['jobsstatus'])) { $modx->setPlaceholders($jobsstatus->toArray()); }Пример: есть таблица user, и экзаменационный лист (где только указан id юзера)
как подставить вместо значения id его имя?
[[!pdoPage?
&class=`ExamSheet`
&tpl=`tpl.examSheet`
&element=`getSheets`
]]
[[!+page.nav]]
[2015-08-06 00:14:09] (ERROR @ /index.php) [pdoTools] Error 42S22: Unknown column 'name' in 'order clause'
У меня нет полей в базе с названием name, соответственно ничего из базы не выводиться через pdoPage.
Пытаюсь поступить так:
[[!pdoPage? &class=`Announcements` &tpl=`tpl.announcements.auto` &element=`getAuto` &leftJoin => `array( 'AutoFields' => array( 'class' => 'AutoFields', 'on' => 'Announcements.id = AutoFields.announcement_id', )` ]][[!pdoPage? &class=`Announcements` &tpl=`tpl.announcements.auto` &element=`getAuto` &leftJoin => `{"AutoFields":{"class":"AutoFields", "on":"Announcements.id = AutoFields.announcement_id"}}` ]]Как-то так$q = $modx->newQuery('Announcements'); //$q->where(array('id' => '1')); $q->leftJoin('AutoFields', 'auto', 'auto.announcement_id =Announcements.id'); $q->select(array('Announcements.*, auto.*')); $q->limit(10); $q->prepare(); $q->stmt->execute(); $res = $q->stmt->fetchAll(PDO::FETCH_ASSOC); echo "<pre>"; print_r($res); echo "</pre>";Ну в дальнейшем с обходом цикла и передачей в чанк массива строки. Данный способ же тоже можно как то разбивать по страницам насколько я понимаю. Есть возможность данный снипет каким то образом вызывать как &element в сниппете pdoPage?А в pdoPage можно любой сниппет передавать. Главное, чтобы он устанавливал плейсхолдер total и понимал limit/offset
Сделал у себя форму для сбора анкетных данных по примеру из этого урока, добавил отправку по е-майл. Всё работало нормально, но через некоторое время форма стала делать submit по нажатию enter в любом поле. За последнее время я делал несколько пробных установок различных дополнений для MODX, возможно причина в этом, поскольку теперь любая форма c Formit делает submit по нажатию enter в любом поле.
Не хотелось бы переустанавливать сайт с нуля, может подскажете идею, как можно отловить причину?
Enter переводит строку только в textarea.
Вот самая обычная форма — без использования любых сниппетов. И она тоже отправляется по нажатию Enter внутри поля: http://jsfiddle.net/002xpxz7/
Т.е. по любому Enter`y срабатывает валидация и отправка, если заполнены все обязательные поля. Если сделать все поля обязательными, то форма никуда не денется, пока не будет заполнена полностью.
Это в принципе, нормально, но у меня не получилось добиться нормальной отправки формы по e-mail без добавления «fi.» к именам полей в плейсхолдерах, и теперь поля с «fi.» стали очищаться при обновлении формы после Enter. Если «fi.» не добавлять, то поля не очищаются. Это специфика Formit`a такая? Как можно побороть эту ситуацию? Пусть форма перегружается, но чтобы данные не чистились и mail отправлялся нормально.
Если человек правильно ввел все поля, но ошибся в e-mail или не ввел какое-то обязательное поле, страница обновится и будет выведена ошибка. Если поля не сохранить, пользователь очень расстроится, что ему придется вводить всё заново.
Но если форма успешно отправлена, поля очищаются, и пользователь не испытывает от этого дискомфорта. Кроме того, он может захотеть отправить форму еще раз, но с другими данными — тогда ему не придется стирать все данные во всех полях.
У меня получилось, что при вводе любого текстового поля страница обновляется и введенные значения теряются, если сделать форму по Вашему образцу и добавить «fi.» к именам полей в плейсхолдерах, как обычно делается в формах для Formit. Т.е. человек в принципе не может ввести форму полностью, потому что при вводе каждого текстового поля оно очищается). Если не добавлять «fi.» к именам полей в плейсхолдерах, то поля не чистятся, но не отправляется нормально копия на e-mail.
В этом и суть проблемы, как добиться, чтобы при добавлении «fi.» к именам полей в плейсхолдерах поля не очищались приобновлении формы.
Более конкретный вопрос- почему Вы в своем примере не используете «fi.» в именах полей в плейсхолдерах?
После отправки формы GET параметр cheet пропадает и URL имеет вид: site.com?id=1.
Как сохранить GET параметр после отправки формы?
К слову, попробовал так сделать, не сработало, чего и следовало ожидать.
В общем, спасибо, Илья, за отзывчивость. Не ожидал столь быстрого ответа)
Возникла потребность добавить в форму идентификатор сессии.
Написал prehook для FormIt:
<?php $hook->setValue('SessId',session_id()); return true;Добавил в форму input с именем SessId, форма срабатывает, но SessId в базу не пишется.
Можешь посоветовать, как добиться, чтобы это поле дописывалось. Наверное, лучше переделать сниппет с вызовом процессора (ExamSheet). У меня форма только создает анкеты, других процессоров нет.
А проблема просто решалась — забыл обновить модель в CMP Generator`e.
Таблицу с данными выводит, но если нажать по ссылке «редактирование», то выводит форму правильно, с теми же данными, что и в строке, единственное, что если отредактировать данные и нажать кнопку сохранить, то перебрасывает опять на эту же форму, только пустую.
Классы создания, редактирования и удаления я создала:
<?php class MetObespCreateProcessor extends modObjectCreateProcessor { public $classKey = 'MetObesp'; public $objectType = 'object'; public function beforeSet() { if (!$this->modx->user->id) return 'Вам нужно авторизоваться'; $this->setProperty('created_by', $this->modx->user->id); return true; } } return 'MetObespCreateProcessor';точно также редакт. и удалениеСниппет MetObesp
if (!$_POST['obesp']) { $processor = 'obesp/create'; } else { $_POST['id'] = $_POST['obesp']; $modx->setPlaceholder('id', $_POST['id']); if ($_POST['remove']) { $processor = 'obesp/remove'; } else { $processor = 'obesp/update'; } } $processorProps = array('processors_path' => $modx->getOption('core_path') . 'components/Uch/processors/'); $response = $modx->runProcessor($processor, $_POST, $processorProps); if ($response->isError()) { $hook->addError('process_error', $response->getMessage()); return false; } return true;И содержимое страницы редактирования:[[!getObespData?]] [[!FormIt? &hooks=`MetObesp,redirect` &redirectTo=3` &validate= 'rabota:required, zatr:required, nachalo:required, konec:required, isp:required' ]] <div class="row"> <div class="col-lg-6" style="margin-left: auto; margin-right: auto; float: none;"> [[!+error.process_error:notempty=`<div class="alert alert-dismissable alert-danger"> <button type="button" class="close" data-dismiss="alert">×</button> <strong>Ошибка!</strong> [[!+error.process_error]] </div>`]] <div class="well"> <form class="bs-example form-horizontal" action="[[~[[*id]]]]" method="post"> <input type="hidden" value="[[!+id]]" name="obesp"> <fieldset> <legend>[[*pagetitle]]</legend> <div class="form-group [[!+error.rabota:notempty=`has-error`]]"> <label for="rabota" class="col-lg-4 control-label">Наименование работы</label> <div class="col-lg-8"> <input type="text" value="[[!+rabota]]" class="form-control" id="rabota" placeholder="Работа" name="rabota"> <span class="help-block">[[!+error.rabota]]</span> </div> </div> <div class="form-group [[!+error.zatr:notempty=`has-error`]]"> <label for="zatr" class="col-lg-4 control-label">Затраты (часов)</label> <div class="col-lg-8"> <input type="int" value="[[!+zatr]]" class="form-control" id="zatr" placeholder="1" name="zatr"> <span class="help-block">[[!+error.zatr]]</span> </div> </div> <div class="form-group [[!+error.nachalo:notempty=`has-error`]]"> <label for="nachalo" class="col-lg-4 control-label">Начало работы</label> <div class="col-lg-8"> <input type="date" value="[[!+nachalo]]" class="form-control" id="nachalo" placeholder="00-00-0000" name="nachalo"> <span class="help-block">[[!+error.nachalo]]</span> </div> </div> <div class="form-group [[!+error.konec:notempty=`has-error`]]"> <label for="konec" class="col-lg-4 control-label">Окончание работы</label> <div class="col-lg-8"> <input type="date" value="[[!+konec]]" class="form-control" id="konec" placeholder="00-00-0000" name="konec"> <span class="help-block">[[!+error.konec]]</span> </div> </div> <div class="form-group [[!+error.isp:notempty=`has-error`]]"> <label for="isp" class="col-lg-4 control-label">Отметка об исполнении</label> <div class="col-lg-8"> <input type="tinyint" value="[[!+isp]]" class="form-control" id="isp" placeholder="-" name="isp"> <span class="help-block">[[!+error.isp]]</span> </div> </div> <div class="form-group"> <div class="col-lg-8 col-lg-offset-4"> <button type="submit" class="btn btn-primary">Сохранить</button> <button type="submit" class="btn btn-danger btn-xs" name="remove" value="remove" onclick="return confirm('Удалить запись?')">Удалить</button> </div> </div> </fieldset> </form> </div> </div> </div>Не могу понять, в чем ошибка?[[!FormIt? &hooks=`MetObesp,redirect` &redirectTo=`3` &validate=`rabota:required, zatr:required, nachalo:required, konec:required, isp:required` ]]Ну и ресурс с ID = 3 это что за ресурс? Может надо поставить &redirectTo=`1`?
В error.log MODX'a пишет после добавления пакета в CMP права на папки 0755, файлы 0644
В принципе все работает, просто немного неприятно видеть ошибки в логе…
[{"partrequest":{"path":"[[++core_path]]components/partrequest/model/"]}}на[{"partrequest":{"path":"/core/components/partrequest/model/"]}}Те же, только в профильЭти ошибки лезут, когда заходишь на страницу CMP Generator в админке.
[ {"partrequest":{"path":"[[++core_path]]components/partrequest/model/partrequest/"}} ][ {"partrequest":{"path":"[[++core_path]]components/partrequest/model/"}} ]проблема была в синтаксисе квадратных скобок, в начале и в конце, недосмотрел, вот что значит копипастить. Тег <code>, если без пробела пододвигает квадратную скобку влево на два символа.Хотя pdoPage с ошибочным синтаксисом в настройке extension_packages отрабатывал нормально и выводил данные из таблицы. Матюкался только CMP Generator.
Спасибо большое за внимание и за советы. :)
[[!Register? &postHooks=`employeesForms` &submitVar=`registerbtn` &activationEmailTpl=`userLgnActivateEmailTpl` &activationEmailSubject=`Спасибо за регистрацию!` &usergroups=`Users::Member` &submittedResourceId=`4` &activationResourceId=`7` &validate=`firstname:required, secondname:required, mail:required` ]]Хочу уточнить, как возможно в xpdo сделать проверку на существование записи в базе данных. И если она существует, то переписывать данные. То есть, пользователь может создать только одну запись и потом ее менять, но новые не создаются.
И второй момент, как привязать условие чтобы у каждого пользователя, например, была возможность создать новую запись раз в году? Например, в 2015 году пользователь записывает данные, в течение года их редактирует, и как только начинается 2016 год, то старая запись за 2015 год становится запрещенной к редактированию и создается новая…
Вопрос, кажется, трудный :( Или подскажите, пожалуйста, где можно про это прочитать и куда копать? Спасибо
[[!pdoPage? &class=`EmployeesForms` &tpl=`tpl.employeesForms` &element=`getForms` &where=`?????????????` ]] [[!+page.nav]]Нужно знать, в каком поле хранятся данные о принадлежности к пользователю
Страницы пользователям создаю по вашей инструкции отсюда: ilyaut.ru/tips-and-tricks/develop-a-social-network-on-modx-revolution/
Получается, нужно как то связать ресурс с соответствующей строкой в БД?
Можно, например, по полю username или email?
Пробую делать так. По идее, он же должен показывать все поля пользователя, у которого username совпадает с firstname. Но выдает по-прежнему полный список.
[[!pdoPage? &class=`ExamSheet` &tpl=`tpl.examSheet` &element=`getSheet` &where=`{«created_by»:[[!+modx.user.id:firstname=`username`]]}` ]] [[!+page.nav]]Может быть дело в этом:А как в этом чанке записать username пользователя (ссылку), который будет выводиться не из базы данных, а из формы или из ресурса?
<tr> <td>[[+id]]</td> <td><a href="[[~67? &sheet=`[[+id]]`]]">['username']</a></td> <td>[[+firstname]]</td> <td>[[+secondname]]</td> <td>[[+email]]</td> </tr>&where=`{"created_by":[[!+modx.user.id]]}`Страница редактирования:
[[!getFormsData?]] [[!FormIt? &hooks=`employeesForms,direct` &successMessage=`Изменения сохранены` &validate=`email:required, firstname:required, secondname: required, username: validate` ]] <p>[[+fi.error.error_message]]</p> <form class="form" action="[[~[[*id]]]]" method="post" enctype="multipart/form-data" > <input name="nospam:blank" type="hidden" /> <input name="resource_id" type="hidden" value="[[+fi.id]]" /> <input id="parent" name="parent" type="hidden" type="text" value="[[+fi.parent]]" /> <div> <label>Username:</label> <span class="error">[[+fi.error.username]]</span> <input name="username" type="text" value="[[+fi.username]]" /> </div> <div> <label>Почта:</label> <span class="error">[[+fi.error.email]]</span> <input name="email" type="text" value="[[+fi.email]]" /> </div> <div> <label>Имя:</label> <span class="error">[[+fi.error.firstname]]</span> <input id="firstname" name="firstname" type="text" value="[[+fi.firstname]]" /> </div> <div> <label>Фамилия:</label> <span class="error">[[+fi.error.secondname]]</span> <input id="secondname" name="secondname" type="text" value="[[+fi.secondname]]" /> </div> <br class="clear" /> <div class="form-buttons">v <input type="submit" value="Отправить" /> </div> </form>Сниппет getFormsData:<?php if ($_GET['forms'] && $forms = $modx->getObject('EmployeesForms', $_GET['forms'])) { $modx->setPlaceholders($forms->toArray()); }if ($_GET['sheet'] && $sheet = $modx->getObject('ExamSheet', $_GET['sheet'])) { $modx->setPlaceholders($sheet->toArray()); }«Соответственно, если вы припишете к адресу страницы добавления get-параметр sheet с id-шником нужного объекта, вы получите форму, заполненную данными объекта. А в сниппете examSheet у нас уже заложено разветвление — если указан id объекта, используется процессор update. „Что то недопонимаю…
Но думаю, что ошибка тут.
if (!$_POST['sheet']) { $processor = 'sheet/create'; } else { $_POST['id'] = $_POST['sheet']; $modx->setPlaceholder('id', $_POST['id']); $processor = 'sheet/update'; }Необходимо вывести информацию из БД текущего пользователя.
Вывод полного списка работает без проблем,
а при такой записи:
[[!pdoPage? &class=`ExamSheet` &tpl=`tpl.examSheet` &element=`getSheets` &where=`{"created_by":[[!+modx.user.id]]}` ]] [[!+page.nav]]результат не приходит и появляется ошибка [2016-04-24 12:58:55] (ERROR @ /index.php) [pdoTools] Error 42S22: Unknown column 'ExamSheet.created_by' in 'where clause'Спасибо! Получилось
На сайте реализована динамическая форма с помощью JS (добавление задач пользователями).
Если каждый раз при изменении БД, мы заново генерируем пакет, то подскажите пожалуйста, как сделать запись каждой задачи в базу данных? Количество задач может быть произвольным.
<?php if (!$_POST['info']) { $processor = 'info/create'; } else { $_POST['id'] = $_POST['info']; $modx->setPlaceholder('id', $_POST['id']); $processor = 'info/update'; } $processorProps = array('processors_path' => $modx->getOption('core_path') . 'components/pers/processors/'); $response = $modx->runProcessor($processor, $_POST, $processorProps); if ($response->isError()) { $hook->addError('process_error', $response->getMessage()); return false; } return true;воторой<?php if ($_GET['info'] && $info = $modx->getObject('PersInfo', $_GET['info'])) { $modx->setPlaceholders($info->toArray());}когда попустим просматриваю пользователей по /index.php?id=2— отображается весь список,
Отображение по /index.php?id=2&info=12 — ничего и не меняется
т.к. данные заносят в таблицу, и так же выводятся, что в общий список что по конткретному id!
например ID=2&info=6 — данные заполняются в форму.
Но при изменении поля любого поля — создается новая строка в таблице а не редактируется предыдущий пользователь!
процессоры добавлены и сниппеты тоже.
if (!$_POST['info']) { $processor = 'info/create'; print "Cобираюсь использовать процессор create"; die(); } else { $_POST['id'] = $_POST['info']; $modx->setPlaceholder('id', $_POST['id']); $processor = 'info/update'; print "Cобираюсь использовать процессор update"; die(); }видимо с модиксом подружится не так уж и просто.
так как
if ($_GET['info'] && $info = $modx->getObject('PersInfo', $_GET['info'])) { $modx->setPlaceholders($info->toArray()); }$_GET[''] запрашивает по дефолту значение после .php?name= а тут…в общем что я не могу понять. = Почему по .php?id=2&info=6 данные выводятся, но не заменяются. хотя $GET задает а $post не получает О_о???, для меня загадка… Причем все ссылки с данным вопросом ведут к вам)))) мне страшно!!!xD
Потому надо ставить
может есть и более правильные способ определения, но я его не нашел.
заместо например это образно, просто не знаю как запросить данные из таблицы modx_user_attributes
В процессоры редактирования и добавления данных можно ли добавить проверку, чтобы администратор-менеджер с каким то id мог редактировать-создавать данные для пользователей, но и другие пользователи не могли работать с данными чужих пользователей?
То есть предполагаю сделать как то так проверку в процессорах редактирования и создания:
public function beforeSet() { if $UserID = 8 return true; (проверяем на айди администратора) (????????????) if (!$this->modx->user->id) return 'Вам нужно авторизоваться'; if ($this->object->get('created_by') != $this->modx->user->id) return 'Вы не можете редактировать чужие записи'; return true; }Или это вообще неправильный подход? и как написать корректно. У меня пока что не работает :(public function beforeSet() { if (!$this->modx->user->id) return 'Вам нужно авторизоваться'; if ($this->modx->user->id == 8) return true; if ($this->object->get('created_by') != $this->modx->user->id) return 'Вы не можете редактировать чужие записи'; return true; }if ($sheet->get('created_by') == $modx->user->id) { $modx->setPlaceholders($sheet->toArray()); } else if ($modx->user->id == 8) { $modx->setPlaceholders($sheet->toArray()); }Тогда точно будет работать.Илья, теперь вот другой вопрос. По прямой ссылке с каким то id, если я авторизована под администратором, я могу увидеть данные пользователя. Я про это: «если вы припишете к адресу страницы добавления get-параметр sheet с id-шником нужного объекта»
В связи с этим вопрос, а как я могу программно сделать эту ссылку. Чтобы например, нажимаешь — «редактировать» и входишь на редактирование какого-то пользователя.
У меня организовано следующим образом: администратор входит на страницу какого то пользователя: просматривает его данные. Это я сделала с помощью GET и where. Администратору выдается список пользователей и он может посмотреть их данные.
Но вот теперь я не могу с страницы просмотра данных пользователя перейти на страницу редактирования его данных. Второй раз GET не получается сделать. Как сделать? Что-то не пойму. Надеюсь, понятно объяснила.
Спасибо.
С чем это может быть связано? Мысли есть?
Вопрос. После сохранения записи отправляет на начальную страницу сайта, а не на текущую
Не могу осилить left join для pdoPage.
Новая
ExamSubjectSheet (modx_exam_subject_sheet)
id (INT), sub_title (TEXT)
Редактируем
ExamSheet (modx_exam_sheet)
subject (INT)
&leftJoin=`{"Subject": { "class": "ExamSubjectSheet", "on": "ExamSheet.subject = Subject.id" } }` &select=`{ "ExamSheet": "*", "Subject": "sub_title" }`Не подскажите как реализовать:
На против каждой записи кнопка, при нажатии ее запись должна копироваться в другую таблицу.
Еще бы немного поподробнее про редактирование и удаление объектов. Никак не получается редактирование и удаление объектов.
Подскажите, как в вашей таблице «Деканат инфо» в строке таблицы вывести значение переменной шаблона,
если Студент — это значение ID ресурса.
Понятно что надо аккуратно составить и шаблон вывода строки и аккуратно передать параметр в сниппет.
Всего-то нужно один параметр ID ресурса передать в сниппет и получить значение TV.
Для одного ресурса таким сниппетом
<?php /* Получаем значение TV по ID ресурса и ID TV */ $where = array( 'contentid' => $idres , 'tmplvarid' => $idtv // ID TV =4 ); $tv = $modx->getObject('modTemplateVarResource', $where); //получен объект TV для $idtv $tvvalue = $tv->get('value'); //по объекту получено значение return $tvvalue;получить значение TV удается, а вот для нескольких строк не получается.Вот пример кода по вашему уроку, даже задача проще:
Форма
[[!getEventData]] [[!FormIt? &hooks=`runnerEvent,redirect` &redirectTo=`[[+id]]` &placeholderPrefix=`` ]] <form action="[[~[[*id]]]]" method="post"> <input type="hidden" name="page_id" value="[[!*id]]"> <input type="hidden" name="user_id" value="[[!+modx.user.id]]"> <input type="hidden" name="title" value="[[!*pagetitle]]"> <div class="form-group"> <button type="submit" class="button medium-btn">Принять участие</button> <button type="submit" class="button medium-btn" name="remove" value="remove" onclick="return confirm('Удалить событие?')">Игнорировать событие</button> </div> </form>Вот сниппет getEventData<?php if ($_GET['event'] && $event = $modx->getObject('RunnerEvent', $_GET['event'])) { $modx->setPlaceholders($event->toArray()); }Вот сниппет runnerEvent<?php if (!$_POST['event']) { $processor = 'event/create'; } else { $_POST['id'] = $_POST['event']; $modx->setPlaceholder('id', $_POST['id']); if ($_POST['remove']) { $processor = 'event/remove'; } else { $processor = 'event/update'; } } $processorProps = array('processors_path' => $modx->getOption('core_path'). 'components/Runner/processors/'); $response = $modx->runProcessor($processor, $_POST, $processorProps); if ($response->isError()) { $hook->addError('process_error', $response->getMessage()); return false; } return true;Вот код из файла класса:
<?php class RunnerEventRemoveProcessor extends modObjectRemoveProcessor { public $classKey = 'RunnerEvent'; public $objectType = 'object'; public function beforeRemove() { if (!$this->modx->user->id) return 'Вам нужно авторизоваться'; //if ($this->object->get('created_by') != $this->modx->user->id) // return 'Вы не можете удалять чужие записи'; return true; } } return 'RunnerEventRemoveProcessor';В чем может быть соль? Куда копать хотя бы расскажите, задача простая, пользователь подписывается на событие, которое создается другими пользователями через снипет Ticket.В табличку кладется Id страницы, пользователя, и заголовок страницы. По вашему уроку порядок, все добавляется, а вот отредактировать не могу, скорее даже удалить. Просто юзер снова подписывается на страницу (добавляется запись новая, дубль), где ошибся? Подскажите направление, место, снипет где не то, я подозреваю вот тут проблема
if (!$_POST['event']) $processor = 'event/create'; все время выполняет это условие и дальше не идет, создает и создает.
Чтобы сниппет знал, что id есть, значит, и объект есть
Единственное удаление только с GET параметром с ID удаляемой страницы работает, можно как то без GET параметра удалить запись из БД? Мы же все равно знаем ID страницы удаляемой.
CMP Generator сгенерировал модель Booking
extension_packages
...,{"Booking":{"path":"[[++core_path]]components/Booking/model/"}}Join все связал и замечательно отображает (отдельное спасибо за урок )Но вдруг возникла непонятная проблема с выводом данных таблиц по отдельности:
выводит ресурсы
не выводит
не выводит
Placeholder не срабатывает :(
полe select
<select class="form-control" id="course" name="course"> <option value="2">Второй курс</option> <option value="1">Первый курс</option> </select>input type=«date»<input type="date" value="[[!+date]]" class="form-control" id="date" name="date" placeholder="2017-01-01 00:00:00"><select class="form-control" id="course" name="course"> [[!pdoResources? &class=`ExamCourse` &sortby=`ExamCourse.course_id` &tpl=`@INLINE <option [[+course:is=`[[+course_id]]`:then=`selected`:else=``]] value="[[+course_id]]">[[+course_title]]</option>` ]] </select>[[!pdoResources? &class=`ExamCourse` &sortby=`ExamCourse.course_id` &tpl=`@INLINE <option selected value="[[+course_id]]">[[+course_title]]</option>` &where=`{"course_id:=":"[[+course]]"}` &limit=`1` ]] [[!pdoResources? &class=`ExamCourse` &sortby=`ExamCourse.course_id` &tpl=`@INLINE <option selected value="[[+course_id]]">[[+course_title]]</option>` &where=`{"course_id:!=":"[[+course]]"}` &limit=`0` ]]<select class="form-control" id="course" name="course"> [[!pdoResources? &class=`ExamCourse` &sortby=`ExamCourse.course_id` &tpl=`@INLINE <option {if $course == '[[+course_id]]'}selected{/if} value="[[+course_id]]">[[+course_title]]</option>` ]] </select>Подскажите пожалуйста как можно студенту например загрузить на сервер свою контрольную? И чтобы с файлами можно было работать как с остальными полями в контексте текущего урока.
Если вопрос в печеньках — пишите на почту.
Буду благодарен.
В результате ничего не происходило, и стало мне любопытно, а как же мне найти ошибку? Искал я эту ошибку долго, а логи пустые.
В общем вопрос такой, как во время разработки мне отлавливать подобные ошибки, с опечатками и прочим?
у меня в консоли эта команда выводит Array всех ресурсов
Ну или задайте параметры прямо в коде:
<?php $scriptProperties = $scriptProperties ?: array(); $scriptProperties['class'] = 'ExamSheet'; $scriptProperties['tpl'] = 'tpl.examSheet'; // чанк всё же создать надо $path = MODX_CORE_PATH. 'components/pdotools/model/pdotools/'; $pdoFetch = $modx->getService('pdofetch','pdoFetch', $path, $scriptProperties); $pdoFetch->setConfig($scriptProperties); return $pdoFetch->run();1. Создала класс Predmet
подключить через системные настройки не смогла
поэтому через сниппет вызываю как
$modx->addPackage('Predmet', $modx->getOption('core_path').'components/Predmet/model/','modx_');сохранение записи по примеру из прошлого урока в таблицу идет. проверила.2. чанк вывода создала
3. на странице вызываю
[[Podkl_Table]] - здесь строка $modx->addPackage('Predmet', $modx->getOption('core_path').'components/Predmet/model/','modx_'); [[!pdoPage? &class=`Predmet` &tpl=`tpl.examSheet` &element=`getSheets` ]] [[!+page.nav]]если без &class=`Predmet` — то все, работает, выводятся ID ресурсовкак только ставлю &class=`Predmet` — результат работы Array
ну что делаю не так?
$modx->addPackage('Predmet', $modx->getOption('core_path').'components/Predmet/model/','modx_'); $res = $modx->getObject('Predmet',1); $output = $res->toArray(); print "<pre>"; print_r($output);вот этот код у меня выводит первую запись таблицыВот если создался класc, который по сути является отражением таблицы, то получается каждая запись в таблице это отдельный объект?
К примеру, вот такой код
<?php $modx->addPackage('Predmet', $modx->getOption('core_path').'components/Predmet/model/','modx_'); $resources = $modx->getCollection('Predmet'); $output = '<p>Всего ресурсов: '.count($resources).'</p>'; foreach ($resources as $k => $res) { $output .= '<p>['.$k.'] => '.$res->get('MO').'</p>'; } print $output;выдает мне все записи таблицы.но хочется освоить все-таки через PdoTools
может я изначально что-то не так сделала (
1. У таблицы обязательно должен быть ID — первичный ключ, автоинкримент
2. У таблицы должен быть префикс, такой же какой задали при создании базы. У меня у всех таблиц стоял префикс edu22_, а новую таблицу я создала с префиксом modx_.Не смогла подключить через системные настройки. Хотя через сниппет подключение шло.
3. Дайте имя таблицы в несколько слов через префикс. У меня сначала была таблица modx_Predmet и класс создавался тоже Predmet, Почему-то никак не смогла работать.
Ну что в итоге. У меня есть таблица edu22_Predmet_Items. Генерирую класс. Он создается как PredmetItems.
Вот тогда дальше все идет как по инструкции:
Чанк для вывода tpl.examSheet
Сниппет подключения класса Podkl_Table
<?php $modx->addPackage('Predmet', $modx->getOption('core_path').'components/Predmet/model/','edu22_');Сниппет для формирования списка getSheets (через PDOTools — но я так и не поняла что конкретно он делает и не проверила, работает ли без него)<?php $path = MODX_CORE_PATH . 'components/pdotools/model/pdotools/'; $pdoFetch = $modx->getService('pdofetch','pdoFetch', $path, $scriptProperties); return $pdoFetch->run();и вызов на странице[[Podkl_Table]] [[!pdoPage? &class=`PredmetItems` &tpl=`tpl.examSheet` &element=`getSheets` ]] [[!+page.nav]]начал выдавать записи. но в механике процесса хочется, конечно, разобраться. почему если пакет и класс назывались одинаково не получилось (спасибо за помощь!
Код из статьи, убрана проверка на авторизацию пользователя.
Уже мозг сломал пытаясь понять как правильно написать, по логике надо писать разбор полей $_POST['sheet'] в examSheet, делать запрос в свою таблицу по значению поля, и если запрос вернул пусто то обрабатывать $processor = 'sheet/create'; но как правильно написать пока не понимаю…
$path = MODX_CORE_PATH . 'components/pdotools/model/pdotools/'; $pdoFetch = $modx->getService('pdofetch','pdoFetch', $path, $scriptProperties); $pdoFetch->setConfig($scriptProperties); return $pdoFetch->run();получить и сделать с ними какие-то операции, например из бд достать число и умножить, после чего только вывести на сайте, как это реализовать???Подскажите, как быть с картинками? Создал объект, всё добавляется и.т.д.
Как мне подгружать картинки к данному объекту? У каждого объекта могут быть фотографии.
Я просто новичек в modx и вообще создании сайтов.
С помощью этой статьи пытаюсь сделать возможность пользователям, залогиниться и добавлять свой контент с премодерацией.
Мой мозг упёрся в две вещи:
1. Допустим, возьмём пример из этой статьи. Я сделал, что по нажатию на имя студента, мы переходим на страницу с подробной информацией. Как сделать нормальный ЧПУ, а не &sheet=id ???
2. Дать возможность пользователю добавлять до 20 фотографий к каждому объекту. Давать названия или подписи под фотографией. И чтобы потом всё это открывалось например fancybox'ом.
Хочется какое-то удобное и безопасное решение!
Tickets — это не просто страницы созданные редактором MarkItUp?
Мне очень понравилась Ваша статья и именно этот способ создания новых объектов, т.к. не создаётся ничего лишнего.
Например, мне после создания очередной записи в таблице легко было реализовать генерацию json файла из этой таблицы. Я излучаю свет от того, как всё это быстро работает. От того насколько всё компактно без всего лишнего.
Как думаете, я смогу с помощью tickets легко всё переделать не потеряв функционал?
Без проблем удалось навесить и свой функционал с помощью TV и xPDO. Конечно, потерял в скорости сайта =(
Никак не могу добиться работоспособности добавления и редактирования записей. Из таблицы записи, добавленные в ручную в таблицу загружаются нормально.
Но при добавлении/удалении через скрипт выдает ошибку:
Fatal error: Cannot declare class DostavkaPochtiCreateProcessor, because the name is already in use in Z:\home\tlk\www\core\components\DostavkaPochti\processors\sheet\create.class.php on line 13
Насколько я понял, почитав в интернете, это могут быть проблемы с настройкой сервера?
Нужна еще небольшая помощь. Если через форму кроме данных подгружать также файл, то как получить доступ к глобальной переменной $_FILE в файл-процессоре?
Подскажи, как в процессоре resource/create проверять записи на дубликаты по полю Имя при создании? Никак не выходит это организовать.
Или это делается не в процессоре???
в процессоре дописываем функцию beforeSave
public function beforeSave() { $c = $this->modx->getCount('ExamSheet',array('student' => $this->getProperty('student'))); if ($c > 0) { return 'The student already exists'; } return true; }Тут подробнее: modx.pro/development/3159Подскажи пожалуйста, можно ли исправить две ошибки при редактировании.
1. Не подставляются данные если в них были содержались кавычки ".
2. Возможно ли при редактировании «выставить» нужную строку select, которая была выбрана при создании?
$sheet = $modx->newObject('SpisokTuristov');
$sheet->save();
В БД появляется новая строка.
А тут проблема:
[[!pdoPage?
&class=`SpisokTuristov`
&tpl=`tpl.SpisokTuristov`
&element=`getSheets`
]]
[[!+page.nav]]
Ничего не выводит. Если удаляю: &class=`SpisokTuristov` то выводит modResource. То есть получается он не может получить &class=`SpisokTuristov`. Как это исправить?
Спасибо! Урок интересный.
$sheet = $modx->newObject('SpisokTuristov');
$sheet->save();
А подскажите что делать если в БД не сохраняются данные введенные в форму? сохраняются просто пустые строки
¯ \ _ (ツ) _ / ¯
Данные из формы не выводятся на страницу, но в базу данных сохраняет все параметры. Так же получается что никак нельзя редактировать и удалять, только через БД
Как сделать чтоб при нажатии кнопки «Сохранить» не перебрасывал на главную страницу?