Webylon 3.1 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • auth
  • Booking
  • cart
    • shipping
    • steppedcheckout
  • Catalog
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • Announcement_Controller
  • AnnouncementHolder_Controller
  • BookingAdminPage_Controller
  • BookingPage_Controller
  • Cart_Controller
  • CartPage_Controller
  • Catalog_Controller
  • CheckoutPage_Controller
  • ChequePayment_Handler
  • ContactsPage_Controller
  • ContentController
  • ContentNegotiator
  • Controller
  • DataObjectManager_Controller
  • DatePickerField_Controller
  • Director
  • DocPage_Controller
  • DocumentsPage_Controller
  • Event_Controller
  • EventHolder_Controller
  • FileDataObjectManager_Controller
  • FindCyrillic_Controller
  • HomePage_Controller
  • LastDoc_Controller
  • LiveCalendarWidget_Controller
  • MapObject_Controller
  • MapObjectGroup_Controller
  • MapPage_Controller
  • MediawebPage_Controller
  • ModelAsController
  • MultiUploadControls
  • NewsArchive
  • Orders1CExchange_Controller
  • Page_Controller
  • Payment_Handler
  • PhotoAlbumManager_Controller
  • Product_Controller
  • ProductSearchPage_Controller
  • ProfilePage_Controller
  • PublHolder_Controller
  • Publication_Controller
  • RatingExtension_Controller
  • RegistrationPage_Controller
  • RemoveOrphanedPagesTask
  • RequestHandler
  • Room_Controller
  • RoomCatalog_Controller
  • RootURLController
  • SapphireInfo
  • Search_Controller
  • Session
  • SimpleOrderPage_Controller
  • SiteMap_Controller
  • SpecialCatalog_Controller
  • SS_HTTPRequest
  • SS_HTTPResponse
  • StartCatalog_Controller
  • SubsitesSelectorPage_Controller
  • VideoBankPage_Controller

Interfaces

  • NestedController

Exceptions

  • SS_HTTPResponse_Exception
  1 <?php
  2 
  3 /**
  4  * Товар в каталоге
  5  *
  6  * @package Catalog
  7  * @author inxo, dvp
  8  */
  9 class Product extends MediawebPage implements ImportInterface{
 10 
 11     //static $icon = 'cms/images/treeicons/element'; //!!!!
 12     static $can_be_root = false;
 13     static $allowed_children = 'none';
 14     static $default_parent = 'Catalog';
 15 
 16     static $db = array(
 17         'ImportID' => 'Varchar', // ID-импорта, используется для связи с 1С
 18         
 19         'Description' => 'Text', // Краткое описание
 20         'Vendor' => 'Varchar', // Производитель
 21         
 22         'Price' => 'CatalogPrice', // Текущая цена товара
 23         'BasePrice' => 'CatalogPrice', // Обычная цена
 24         'CostPrice' => 'CatalogPrice', // Цена по акции
 25         
 26         'Available' => 'Boolean', // Флаг "в наличии"
 27         'AllowPurchase' => 'Boolean', // Флаг "можно заказывать"
 28     );
 29 
 30     static $defaults = array(
 31         'ShowInMenus' => 0,
 32         'ShowOnlyInTab' => 1,
 33         'Available' => 1,
 34         'AllowPurchase' => 1,
 35         'BasePrice' => 0,
 36         'CostPrice' => 0,
 37     );
 38 
 39     static $has_one = array(
 40         'Photo' => 'Image',
 41     );
 42 
 43     static $indexes = array(
 44         'ImportID' => true,
 45         'Price' => true,
 46         'Vendor' => true,
 47     );
 48 
 49     static $summary_fields = array('ImportID', 'Title', 'BasePrice', 'CostPrice', 'Vendor', 'Available', 'AllowPurchase');
 50 
 51     static $fulltext_fields = array('Title', 'Content', 'Description', 'BasePrice', 'CostPrice', 'Vendor');
 52 
 53     static $searchable_fields = array(
 54         'Title' => 'PartialMatchFilter',
 55         'ImportID' => 'ExactMatchFilter',
 56         'Available' => array('filter'=>'ExactMatchFilter'),
 57         'AllowPurchase' => array('filter'=>'ExactMatchFilter'),
 58         'Price' => array('field'=>'RangeField', 'filter'=>'WithinRangeFilter'), 
 59         'Vendor' => array('field'=>'CheckboxSetField', 'filter'=>'ExactMatchMultiFilter'),
 60     );
 61 
 62     static $filter_allowed = true;
 63 
 64     static function disable_filter($val = true) {
 65         self::$filter_allowed = ! $val;
 66     }
 67 
 68     static function filter_allowed() {
 69         return self::$filter_allowed;
 70     }
 71 
 72     /**
 73      * Позволяет задать поля для фильтра товаров
 74      * Фильтр исопльзуется в админке и при фильтрации товаров в каталоге
 75      * 
 76      * @param array $data 
 77      */
 78     static function set_searchable_fields(array $data) {
 79         static::$searchable_fields = $data;
 80     }
 81 
 82     /**
 83      * Возвращает текущие настроки полей для фильтрации
 84      * 
 85      * @return array
 86      */
 87     static function get_searchable_fields() {
 88         return static::$searchable_fields;
 89     }
 90 
 91     /**
 92      * Настройки сортировки товаров в рубриках
 93      * Формат: служебное_имя => "строка для ORDER BY"
 94      */
 95     private static $sort_options = array('title' => 'Title ASC', 'pricea' => 'Price ASC', 'priced' => 'Price DESC');
 96 
 97     /**
 98      * Изменяет список сортировок товаров. 
 99      * Используется в _config.php для настройки параметров каталога
100      * 
101      * @param array $data - новый список сортировок
102      */
103     static function set_sort_options(Array $data) {
104         self::$sort_options = $data;
105     }
106 
107     /**
108      * Возвращает текущий список сортировок товаров
109      * 
110      * @return array - текущий список сортировок
111      */
112     static function get_sort_options() {
113         return self::$sort_options;
114     }
115 
116     /**
117      * Возвращает выражение order by для выбранной сортировки
118      *
119      * @return string
120      */
121     static function sort_options_orderby($sort) {
122         $options = self::get_sort_options();
123         return (array_key_exists($sort, $options)) ? $options[$sort] : '';
124     }
125 
126     /**
127      * Возвращает локализованный список сортировок для использования в селектах
128      * 
129      * @param bool $addDefault - добавлять ли пункт "по-умолчанию"
130      * 
131      * @return array - список для селектов
132      */
133     static function sort_options_dropdown_map($addDefault = false) {
134         $options = self::get_sort_options();
135         if (!$options) return array();
136 
137         $map = array();
138         if ($addDefault) {
139             $map[''] = _t('Product.SortOption_default', 'Default');
140         }
141         foreach ($options as $key => $val) {
142             $map[$key] = _t('Product.SortOption_'.$key, ucfirst($key));
143         }
144         return $map;
145     }
146 
147     /**
148      * Возвращает локализованный список полей фильтрации
149      * 
150      * @return array - список для CheckBoxSet
151      */
152     static function filter_fields_list() {
153         if (!self::filter_allowed()) return array();
154         $filterFields = array(); 
155         $product = singleton(get_called_class());
156         foreach (array_keys($product->searchableFields()) as $field) {
157             $filterFields[$field] = $product->fieldLabel($field);
158         }
159         
160         return $filterFields;
161     }
162 
163     /** 
164     * устанавливает поведение при нулевой (0) цене продукта 
165     * если false, то не добавлять в корзину
166     * если true, то перенаправлять на 
167     **/
168     static $order_empty_price = false;
169 
170     static function allow_order_empty_price($val = true) {
171         self::$order_empty_price = $val;
172     }
173 
174     /**
175      * Класс кнопки заказа, класс должен реализовать OrderButtonInterface
176      */
177     static $order_button_class = 'SimpleOrderButton';
178 
179     static function set_order_button_class($value) {
180         self::$order_button_class = $value;
181     }
182 
183     /**
184      * Класс кнопки заказа для товаров без цены, класс должен реализовать OrderButtonInterface
185      */
186     static $empty_order_button_class = 'SimpleOrderButton';
187 
188     static function set_empty_order_button_class($value) {
189         self::$empty_order_button_class = $value;
190     }
191 
192     public function defaultSearchColumns() {
193         return implode(',', $this->stat('fulltext_fields'));
194     }
195 
196     public function __construct($record = null, $isSingleton = false) {
197         if (!isset($record['Price']) && isset($record['ID'])) {
198             $p = DataObject::get_by_id('Product', $record['ID']);
199             $record = $p->record;
200         }
201         parent::__construct($record, $isSingleton);
202     }
203     
204     /**
205      * Возвращаем основное фото товара
206      * Если есть has_one Photo, то его, иначе первое из прикрепленной галереи
207      * 
208      * @return Image
209      */
210     
211     function MainPhoto() {
212         if ($this->PhotoID && $this->Photo()->ID) {
213             return $this->Photo();
214         }
215         if ($this->Photos()->Count()) {
216             return $this->Photos()->First()->Photo();
217         }
218         return false;
219     }
220     
221     /**
222      * Список всех фото товара
223      * Если есть has_one Photo, то вставляем его первым
224      * 
225      * @return ComponentSet
226      */
227     
228     function AllPhotos() {
229         $photos = $this->Photos();
230         if ($this->PhotoID && $this->Photo()->ID) {
231             $mainPhoto = new MediawebPage_Photo();
232             $mainPhoto->Caption = $this->Title;
233             $mainPhoto->MediawebPageID = $this->ID;
234             $mainPhoto->PhotoID = $this->Photo()->ID;
235             $photos->unshift($mainPhoto);
236         }
237         return $photos;
238     }
239     
240     /*--------------- Функции для импорта -------------------*/    
241     static function import_find($importID) {
242         return DataObject::get_one('Product', "ImportID = '" . Convert::raw2sql($importID) . "'");
243     }
244     
245     /**
246      * Список полей, которые могут быть в данных импорта  !!!!! У текущего Product нет поля SKU!!!!!
247      */  
248     static $possibleFields = array(
249         'Title', 'Available', 'AllowPurchase', 'BasePrice', 'CostPrice', 'Vendor', 'SKU', 'Description', 'Content', 'URLSegment', 'MenuTitle', 'MetaTitle', 'MetaDescription', 'MetaKeywords', 'Sort'
250     );
251     
252     /**
253      * Добавление полей, которые могут быть в данных импорта 
254      *
255      * @param array $fields
256      */
257     static function addPossibleFields($fields) {
258         if ($fields)
259             foreach($fields as $field)
260                 self::$possibleFields[] = $field;
261     }
262     
263     /**
264      * Проверяем булевые поля и задаем им корректные значения
265      *
266      * @param $value
267      */
268     static function import_parse_bool_value($value) {       
269         if (in_array($value, array('true', '1'))) {
270             return 1;
271         }
272         if (in_array($value, array('false', '0'))) {
273             return 0;
274         }
275         return null;
276     }
277 
278     function getProductAdminLink(){
279         return "<a target='_blank' href='/admin/show/{$this->ID}'>{$this->Title}</a>";
280     }       
281     
282      /**
283      * Обновляет объект,
284      * @param $importLog - объект для протоколиорвания импорта (или сама задача), для возможности записать сообщения об ошибках
285      * @param $data - массив с данными для импорта
286      * @return bool - флаг можно ли продолжать импорт
287      */
288     function importUpdate($importLog, $data) {
289         if (!$this->importValidate($importLog, $data)) {
290             return false;
291         }
292             
293         $rs = $this->extend('onBeforeImport', $importLog, $data);
294         if ($rs && max($rs) == false) return false;
295         
296         $this->ImportID = $data['id'];
297         foreach(self::$possibleFields as $field) {
298             if (isset($data[$field]))
299             $this->{$field} = Convert::raw2sql($data[$field]);
300         }
301 
302         //цепляем фотку
303         if (isset($data['Photo'])) {
304             $this->PhotoID = ($data['Photo']) ? $data['Photo']->ID : 0;
305         }   
306         
307         //Обрабатываем списочные параметры (если есть)
308         if (isset($data['params'])) {
309             foreach($data['params'] as $param) {
310                 // !!! то есть не проверяем есть ли такое поле и разрешено оно для записи?
311                 $name = Convert::raw2sql($param->attributes()->name);               
312                 //Проверяем, есть ли такое поле у Product
313                 if ($this->db($name)) {                 
314                     $value = Convert::raw2sql($param);
315                     $this->{$name} = $value;
316                 } else {
317                     $importLog->addLog("У товара не определено поле {$name}!", 'error');
318                 }
319             }
320         }           
321         //Сначала сохраняем, чтоб потом привязывать изображения и файлы
322         if (!$this->ID) //не обновляем родителя у уже импортированных товаров  (для возможности ручного изменения структуры каталога на сайте).
323             $this->ParentID = ($data['ParentID'] !== false) ? $data['ParentID'] : 0;
324                             
325         if (isset($data['ParentID']) && $data['ParentID'] === false) { // Если нету родителя, хотя должен быть (не корневой раздел), то не публикуем
326             $this->write();
327         } else {
328             $this->doPublish();
329         }           
330         
331         //Обрабатываем списочные фотографии (если есть)
332         // !!!!!!
333         if ($data['Photos']) {
334             //прикрепляем фотки
335             $oldPhotos = $this->Photos();
336             $oldPhotosIDs = $oldPhotos->getIdList(); //сохраняем список ID прикрепленных фоток
337             $this->Photos()->removeAll();
338 
339             foreach($data['Photos'] as $importedImage) {
340                 if ($mwPhoto = $oldPhotos->find('PhotoID', $importedImage->ID)) {
341                     unset($oldPhotosIDs[$mwPhoto->ID]); //если фотка осталась, то удаляем ее ID из списка
342                 } else {
343                     $mwPhoto = new MediawebPage_Photo();
344                     $mwPhoto->PhotoID = $importedImage->ID;
345                 }
346                 $mwPhoto->MediawebPageID = $this->ID; //Цепляем к Product
347                 $mwPhoto->Caption = $importedImage->Title;
348                 $mwPhoto->write();
349             }
350 
351             if (count($oldPhotosIDs) > 0) {
352                 foreach($oldPhotosIDs as $oldPhotosID) { // все, что осталось в списке - чистим!
353                     if ($oldPhoto = $oldPhotos->find('ID', $oldPhotosID)) {
354                         $oldPhoto->delete();
355                     }
356                 }
357             }
358         }
359         
360         //Обрабатываем списочные файлы (если есть)
361         if ($data['Files']) {
362             //прикрепляем файлы
363             $oldFiles = $this->Files();
364             $oldFilesIDs = $oldFiles->getIdList(); //сохраняем список ID прикрепленных фоток
365             $this->Files()->removeAll(); 
366 
367             foreach($data['Files'] as $importedFile) {
368                 if ($mwFile = $oldFiles->find('AttachID', $importedFile->ID)) {
369                     unset($oldFilesIDs[$mwFile->ID]); //если фотка осталась, то удаляем ее ID из списка
370                 } else {
371                     $mwFile = new MediawebPage_File();
372                     $mwFile->AttachID = $importedFile->ID; //Цепляем к Product
373                 }
374                 $mwFile->Caption = $importedFile->Title;
375                 $mwFile->MediawebPageID = $this->ID; //Цепляем к Product
376                 $mwFile->write();
377             }
378 
379             if (count($oldFilesIDs) > 0) {
380                 foreach($oldFilesIDs as $oldFilesID) { // все, что осталось в списке - чистим!
381                     if ($oldFile = $oldFiles->find('ID', $oldFilesID)) {
382                         $oldFile->delete();
383                     }
384                 }
385             }
386         }
387 
388         //Обрабатываем списочные спец.каталоги (если есть)
389         if ($data['SpecialCatalogs']) {
390             $productSpecialCatalogs = array(); //список ID-шники спецкаталогов, в которых изначально был продукт
391             //складываем в список ID всех спец.каталогов, в которых есть товар
392             $allSpecialCatalogs = DataObject::get('SpecialCatalog');
393             if ($allSpecialCatalogs) {
394                 foreach($allSpecialCatalogs as $specialCatalog) {
395                     $specialCatalogProducts = $specialCatalog->LinkedProducts()->getIdList();
396                     if (isset($specialCatalogProducts[$this->ID])) {
397                         $productSpecialCatalogs[$specialCatalog->ID] = $specialCatalog->ID;
398                     }
399                 }
400             }
401 
402             //Добавляем товар в указанные спец.каталоги
403             foreach($data['SpecialCatalogs'] as $specialCatalog) {
404                 if (isset($productSpecialCatalogs[$specialCatalog->ID])) { //если товар уже есто в новом спец.каталоге
405                     unset($productSpecialCatalogs[$specialCatalog->ID]); //то удаляем ID спец.каталога из списка
406                 } else {
407                     $specialCatalog->LinkedProducts()->add($this); // иначе добавляем товар в спец.каталог
408                 }
409             }
410 
411             if (count($productSpecialCatalogs)) {
412                 foreach($productSpecialCatalogs as $productSpecialCatalog) { //чистим все, что осталось в списке
413                     if ($oldSpecialCatalog = $allSpecialCatalogs->find('ID', $productSpecialCatalog)) {
414                         $oldSpecialCatalog->LinkedProducts()->remove($this);
415                     }
416                 }
417             }
418         }
419         $this->extend('onAfterImport');
420         return true;
421     }
422 
423     /**
424      * Проверяет данные полей объекта на соответствие типам
425      * @param $importLog - объект для протоколиорвания импорта (или сама задача), для возможности записать сообщения об ошибках     
426      * @return bool - флаг можно ли продолжать импорт
427      */
428     function importValidate($importLog, & $data) {
429         // !!! по идее Title обязательно отлько для новых товаров
430         if ((!$this->Title) && (!isset($data['Title']) || trim($data['Title']) == '')) { // если у каталога нет Title и Title нет в импорте, то ругаемся
431             $importLog->addLog("Товар с id='{$data['id']}' не имеет названия!", 'error');
432             return false;           
433         }       
434         if (isset($data['Title']) && trim($data['Title']) == '') { // если тег <Title> задан, но пустой, то ругаемся
435             $importLog->addLog("Товар с id='{$data['id']}' не имеет названия!", 'error');
436             return false;           
437         }   
438         if (isset($data['Available'])) {
439             $rs = self::import_parse_bool_value($data['Available']);
440             if ($rs === null) {
441                 $importLog->addLog(sprintf(_t('Product.ImportBadBoolValue'), "Наличие товара на складе", $data['Title']), 'warning');
442             } else {
443                 $data['Available'] = $rs; 
444             }           
445         }
446         if (isset($data['AllowPurchase'])) {
447             $rs = self::import_parse_bool_value($data['AllowPurchase']);
448             if ($rs === null) {
449                 $importLog->addLog(sprintf(_t('Product.ImportBadBoolValue'), "Разрешено покупать", $data['Title']), 'warning');
450             } else {
451                 $data['AllowPurchase'] = $rs; 
452             }           
453         }   
454         if (isset($data['BasePrice']) && !is_numeric($data['BasePrice'])) {
455             $importLog->addLog("Параметр 'Цена' товара {$data['Title']} должен быть числовым!", 'error');
456             return false;
457         }   
458         if (isset($data['CostPrice']) && !is_numeric($data['CostPrice'])) {
459             $importLog->addLog("Параметр 'Цена по акции' товара {$data['Title']} должен быть числовым!", 'error');
460             $data['CostPrice'] = 0;
461         }       
462         if (isset($data['Sort']) && $data['Sort'] != (int)$data['Sort']) {
463             $importLog->addLog("Параметр Sort товара {$data['Title']} не является целым числом!", 'warning');
464             $data['Sort'] = 0; 
465         }
466         
467         $rs = $this->extend('importValidate', $importLog, $data);       
468         if ($rs && max($rs) == false) return false;
469         
470         return true;
471     }
472     
473      /**
474      * Выполняет удаление всех объектов перед импортом
475      * Удаляем во всех стейджах
476      */
477     function importClearAll($importLog) {
478         $oldMode = Versioned::get_reading_mode();
479         
480         Versioned::reading_stage('Stage');
481         $products = DataObject::get('Product');
482         if ($products)
483             foreach($products as $product) {
484                 $product->doUnpublish();
485                 $product->delete();
486             }
487             
488         Versioned::reading_stage('Live');
489         $products = DataObject::get('Product');
490         if ($products)
491             foreach($products as $product) {
492                 $product->doUnpublish();
493                 $product->delete();
494             }
495             
496         Versioned::set_reading_mode($oldMode);
497     }       
498     /*--------------- Конец функций для импорта -------------------*/
499     
500         
501 
502     public function getCMSFields() {
503         SiteTree::disableCMSFieldsExtensions();
504         $fields = parent::getCMSFields();
505         SiteTree::enableCMSFieldsExtensions();
506         
507         $fields->replaceField('ParentType', new HiddenField('ParentType', '', 'subpage'));
508         
509         $fields->replaceField('Title', new TextField('Title', _t('Product.db_Title', 'Наименование')));
510         // фильтруем каталоги
511         // !!! сейчас показывается только куст текущего каталога
512         $cat = $this->Parent();
513         while ($cat->Parent() && $cat->Parent()->ClassName == self::$default_parent) {
514             $cat = $cat->Parent();
515         }
516         $parentIDField = new TreeDropdownField('ParentID', $this->fieldLabel('ParentID'), 'SiteTree');
517         $parentIDField->setTreeBaseID($cat->ParentID);
518         $parentIDField->setFilterFunction(create_function('$node', 'return ($node->ClassName == "'.self::$default_parent.'");')); //  || $node->ClassName == "StartCatalog"
519         
520         $fields->replaceField('ParentID', $parentIDField);
521 
522         $fields->addFieldToTab('Root.Content.Main', new NumericField('BasePrice', $this->fieldLabel('BasePrice')));
523         $fields->addFieldToTab('Root.Content.Main', new NumericField('CostPrice', $this->fieldLabel('CostPrice')));
524 
525         $fields->addFieldToTab('Root.Content.Main', new CheckboxField('Available', $this->fieldLabel('Available')));
526         $fields->addFieldToTab('Root.Content.Main', new CheckboxField('AllowPurchase', $this->fieldLabel('AllowPurchase')));
527         
528         $fields->addFieldToTab('Root.Content.Main', $imgField = new ImageField('Photo', $this->fieldLabel('Photo')));
529         if ($folder = $this->getAssociatedFolder()) {
530             $folderPath = substr_replace(str_replace(ASSETS_DIR.'/', '', $folder->Filename), "", -1);
531             $imgField->setFolderName($folderPath);
532         }
533         
534         $fields->addFieldToTab('Root.Content.Main', new TextField('Vendor', $this->fieldLabel('Vendor')));
535         $fields->addFieldToTab('Root.Content.Main', new TextField('ImportID', $this->fieldLabel('ImportID')));
536         $fields->addFieldToTab('Root.Content.Main', new TextareaField('Description', $this->fieldLabel('Description')));
537 
538         if (HtmlEditorConfig::get_active() != 'cms') {
539             HtmlEditorConfig::set_active('cms');
540         }
541         
542         $fields->addFieldToTab('Root.Content', new Tab("FullContent", $this->fieldLabel('Content')), 'Metadata');
543         $fields->addFieldToTab('Root.Content.FullContent', $fields->dataFieldByName('Content'));
544 
545         // !!! TODO каталоги из виртуальных товаров
546         
547         $this->extend('updateCMSFields', $fields);
548         
549         $this->extend('hideCMSFields', $fields); // extend для скрытия полей из админки
550         return $fields;
551     }
552 
553     public function getCMSActions() {
554         Requirements::javascript('webylon/javascript/SubpageAction.js');
555         $fields = parent::getCMSActions();
556         $fields->push(new HiddenField('backLink', '', $this->Parent()->ID));
557         $fields->insertFirst(new FormAction('goBack', _t('Product.BACKBUTTON', 'Back')));
558         return $fields;
559     }
560 
561     public function populateDefaults() {
562         parent::populateDefaults();
563 
564         $sc = SiteConfig::current_site_config();
565         $this->ShowInMenus = ($sc->ProductShowInTree) ? 1 : 0;
566         $this->ShowOnlyInTab = ($sc->ProductShowInTree) ? 0 : 1;
567     }
568 
569     function onBeforeWrite() {
570         parent::onBeforeWrite();
571         if ($this->isChanged('BasePrice') || $this->isChanged('CostPrice')) {
572             $this->Price = ($this->CostPrice > 0) ? $this->CostPrice : $this->BasePrice;
573         }
574         $sc = SiteConfig::current_site_config();
575         if ($sc->ProductShowInTree) {
576             $this->ShowOnlyInTab = 0;
577         }
578         else {
579             $this->ShowInMenus = 0;
580         }
581     }
582 
583     /**
584      * Список виртуальных товаров связанных с текущим
585      * 
586      * @return DataObjectSet
587      */
588     function VirtualPages() {
589         if (!$this->ID)
590             return null;
591         if (class_exists('Subsite')) {
592             return Subsite::get_from_all_subsites('VirtualProduct', "\"CopyContentFromID\" = " . (int) $this->ID);
593         } else {
594             return DataObject::get('VirtualProduct', "\"CopyContentFromID\" = " . (int) $this->ID);
595         }
596     }
597 
598     public function canCreate($member = null) {
599         return (DataObject::get_one('Catalog')) ? parent::canCreate($member) : false;
600     }
601 
602     function fieldLabels($includerelations = true) {
603         $labels = parent::fieldLabels($includerelations);
604         $labels['Title'] = _t('Product.db_Title', 'Title');
605         return $labels;
606     }
607 
608     /**
609      * Возвращает класс для кнопки заказа товара в зависимости от цены и значений Product::$empty_order_button_class и Product::$order_button_class
610      * 
611      * @return string
612      */
613     function оrderButtonClass() {
614         if (!$this->AllowPurchase)
615             return false;       
616             
617         $class = false;
618         if ($this->Price == 0) {
619             if (!self::$order_empty_price)
620                 return false;
621             $class = self::$empty_order_button_class;
622         }
623         $class = self::$order_button_class;
624         $this->extend('updateOrderButtonClass', $class);
625         return $class;
626     }
627 
628     /**
629      * Возвращает объект для генерации кнопки заказа
630      * 
631      * @return OrderButtonInterface
632      */
633     function OrderButton() {
634         $buttonClass = $this->оrderButtonClass();
635         if (!$buttonClass) 
636             return false;
637         return new $buttonClass($this->ID);
638     }
639 
640     /**
641      * Возвращает html для кнопки заказа 1 товара
642      * 
643      * @return string
644      */
645     function OrderButtonOne() {
646         if ($button = $this->OrderButton())
647             return $button->One();
648         return false;
649     }
650 
651     /**
652      * Возвращает html для кнопки заказа товара с оплем для кол-ва
653      * 
654      * @return string
655      */
656     function OrderButtonWithNum() {
657         if ($button = $this->OrderButton())
658             return $button->WithNum();
659         return false;
660     }
661 
662     /**
663      * возвращает цену товара без скидок
664      * 
665      * @return CatalogPrice
666      */
667 
668     function getRealPrice() {
669         $price = $this->Price;
670         $this->extend('updatePrice', $price);
671         return $price;
672     }
673 
674     /**
675      * Возвращает центу товара с учетом скидок
676      * 
677      * @param bool $onlyreal - вернуть только 1 цену
678      * 
679      * @return mixed
680      */
681     function Price($onlyreal=false) {
682         // Значения по умолчанию
683         $real = $this->getRealPrice();
684         $client = $real;
685         $delta = 0;
686         $percent = 0;
687         $decimal = 2;
688         
689         if ($this->CostPrice == 0) {
690             // персональная скидка
691             $member = Member::currentUser();
692             if ($member){
693                 if ($member->hasMethod('getPersonalDiscount') && $percent < $member->getPersonalDiscount()) {
694                     $percent = $member->getPersonalDiscount();
695                 }
696                 // групповые скидки
697                 if ($groups = $member->Groups()) {
698                     foreach ($groups as $g) {
699                         if ($g->hasMethod('getDiscount') && $percent < $g->getDiscount()) {
700                             $percent = $g->getDiscount();
701                         }
702                     }
703                 }
704             }
705             
706             $this->extend('updateDiscount', $percent);
707         }
708 
709         $sc = SiteConfig::current_site_config();
710         $ceil = $sc->CatalogDiscountCeil;
711         
712         if ($ceil) {
713             $real = ceil($real);
714             $client = ceil($client);
715             $decimal = 0;
716         }
717 
718         if ($percent) {
719             $delta = ($ceil) ? ceil(($real / 100) * $percent) : round(($real / 100) * $percent, 2);
720             $client = $real - $delta;
721         }
722         
723         if ($onlyreal !== false) {
724             return $client;
725         }
726         
727         return new ArrayData(array(
728             'Real' => DBField::create('CatalogPrice', $real),
729             'Client' => DBField::create('CatalogPrice', $client),
730             'Delta' => DBField::create('CatalogPrice', $delta),
731             'Percent' => $percent,
732             'Discount' => ($percent > 0) ? 1 : 0,
733             'Currency' => $sc->CatalogCurrency,
734         ));
735     }
736 
737     function HasDiscount() {
738         return ($this->CostPrice > 0);
739     }
740 
741     function Currency() {
742         return SiteConfig::current_site_config()->CatalogCurrency;
743     }
744 
745     function getOrderItem() {
746         //!!! TODO генерировать OrderItem самим
747     }
748 }
749 
750 class Product_Controller extends MediawebPage_Controller {
751     private $siblings = null;
752     private $next = null;
753     private $prev = null;
754 
755     /**
756      * Возвращает "братьев" товара
757      * 
758      * @return DataObjectSet
759      */
760     function Siblings() {
761         if (!isset($this->siblings)) {
762             $catalog = new Catalog_Controller($this->Parent());
763             $catalog->init();
764             $this->siblings = $this->Parent()->filteredProducts($catalog->CurrentSort, $catalog->FilterData);
765         }
766         return $this->siblings;
767     }
768 
769     /**
770      * Возвращает следующий товар в списке
771      * 
772      * @return Product
773      */
774     function NextProduct() {
775         if (!isset($this->next)) {
776             $list = $this->Siblings()->toArray();
777             while ($row = array_shift($list)) {
778                 if ($row->ID == $this->dataRecord->ID) break;
779             }
780             $this->next = array_shift($list);
781         }
782         return $this->next;
783     }
784 
785     /**
786      * Возвращает предыдущий товар в списке
787      * 
788      * @return Product
789      */
790     function PrevProduct() {
791         if (!isset($this->prev)) {
792             $this->prev = false;
793             $list = $this->Siblings()->toArray();
794             $prev = array_shift($list);
795             while ($row = array_shift($list)) {
796                 if ($row->ID == $this->dataRecord->ID) {
797                     $this->prev = $prev;
798                     break;
799                 }
800                 $prev = $row;
801             }
802         }
803         return $this->prev;
804     }
805 }
806 
807 
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.1 API Docs API documentation generated by ApiGen 2.8.0