Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737
Форумы портала PHP.SU :: PHP 7 Использование контроля скалярных типов

 PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (2): [1] 2 »   

> Описание: интересует мнение профессионалов
Prizma
Отправлено: 22 Апреля, 2016 - 17:01:21
Post Id



Посетитель


Покинул форум
Сообщений всего: 463
Дата рег-ции: Июнь 2012  
Откуда: Санкт-Петербург


Помог: 5 раз(а)




И так есть класс Product экземпляр которого создается по product_id.

Задумка такая.. Использовать возможности PHP 7, а именно поддержку контроля скалярных типов данных, при выборке из бд, для приведения всех полей к нужному типу.

Вот как это получилось у меня:
PHP:
скопировать код в буфер обмена
  1. namespace Core\Market\Products;
  2.  
  3. use Core\DataBase\Mysql;
  4. use Core\Market\System\Constants\MTables;
  5.  
  6. class Product
  7. {
  8.     use Mysql;
  9.  
  10.     private $id;
  11.     private $producerId;
  12.     private $authorId;
  13.     private $currencyId;
  14.     private $price;
  15.     private $inStockAmount;
  16.     private $name;
  17.     private $deliveryDays;
  18.     private $shortDesc;
  19.     private $seoDesc;
  20.     private $seoKw;
  21.     private $created;
  22.     private $modified;
  23.     private $objProperties;
  24.  
  25.     public function __construct(int $productId)
  26.     {
  27.         $sql = 'SELECT producer_id, author_id, currency_id, price, in_stock_amount, delivery_days, name, short_desc, '.
  28.             'seo_desc, seo_kw, created, modified, obj_properties FROM '.MTables::PRODUCTS.' WHERE id = '.$productId;
  29.         $q = self::db()->prepare($sql);
  30.         $q->execute();
  31.         if($r = $q->fetch(\PDO::FETCH_NUM)) {
  32.             $this->id = $productId;
  33.             list($p1,$p2,$p3,$p4,$p5,$p6,$p7,$p8,$p9,$p10,$p11,$p12,$p13) = $r;
  34.             $this->setParams($p1,$p2,$p3,$p4,$p5,$p6,$p7,$p8,$p9,$p10,$p11,$p12,$p13);
  35.         }else{
  36.             throw new \Exception('product '.$productId.' was not found');
  37.         }
  38.     }
  39.  
  40.     private function setParams(int $producerId, int $authorId, int $currnecyId, float $price, int $inStockAmount,
  41.                                int $deliveryDays, string $name, string $shortDesc, string $seoDesc, string $seoKw,
  42.                                int $created, int $modified, int $objProperties)
  43.     {
  44.         $this->producerId = $producerId;
  45.         $this->authorId = $authorId;
  46.         $this->currencyId = $currnecyId;
  47.         $this->price = $price;
  48.         $this->inStockAmount = $inStockAmount;
  49.         $this->deliveryDays = $deliveryDays;
  50.         $this->name = $name;
  51.         $this->shortDesc = $shortDesc;
  52.         $this->seoDesc = $seoDesc;
  53.         $this->seoKw = $seoKw;
  54.         $this->created = $created;
  55.         $this->modified = $modified;
  56.         $this->objProperties = $objProperties;
  57.     }
  58.  
  59.     static public function issetProduct(int $productId)
  60.     {
  61.         $q = self::db()->prepare('SELECT COUNT(*) as rows FROM '.MTables::PRODUCTS.' WHERE `id` = '.$productId);
  62.         $q->execute();
  63.         $r = $q->fetch(\PDO::FETCH_ASSOC);
  64.         return ($r['rows'] > 0);
  65.     }
  66. }

класс Mysql:
PHP:
скопировать код в буфер обмена
  1. namespace Core\DataBase;
  2.  
  3.  
  4. use PDO;
  5.  
  6. trait Mysql
  7. {
  8.     static private $db_driver = 'mysql';
  9.     static private $db_host = 'localhost';
  10.  
  11.     static private $db_user = 'root';
  12.     static private $db_password = '';
  13.     static private $db_name = 'db_name';
  14.     static private $db_setting = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES "utf8"');
  15.  
  16.     static private $db;
  17.  
  18.     /**
  19.      * @return PDO
  20.      */
  21.     static protected function db()
  22.     {
  23.         if(is_null(self::$db)) {
  24.             try {
  25.                 self::$db = new PDO(self::$db_driver . ':host=' . self::$db_host . ';dbname=' . self::$db_name, self::$db_user, self::$db_password, self::$db_setting);
  26.             } catch (\Exception $e) {
  27.                 die('Fail connection to database');
  28.             }
  29.         }
  30.         return self::$db;
  31.     }
  32. }
  33.  

Что вы думаете о такой реализации, уважаемые профи?

(Отредактировано автором: 22 Апреля, 2016 - 20:33:58)

 
My status
 Top
LIME
Отправлено: 22 Апреля, 2016 - 17:16:25
Post Id


Активный участник


Покинул форум
Сообщений всего: 10732
Дата рег-ции: Нояб. 2010  


Помог: 322 раз(а)




С точки зрения ооп все криво
И какая выгода использовать typehint?
Можно явно привести в list
А еще есть extract()
Лучше завести класс работающий с хранилищем
Тогда для смены хранилища не придется менять класс модели
Принцип open/close SOLID
(Добавление)
Typehint надо использовать в классах использующих эти данные
Там будет уместно
(Добавление)
Datamapper... ознакомся...
 
 Top
teddy
Отправлено: 22 Апреля, 2016 - 19:52:47
Post Id


Участник


Покинул форум
Сообщений всего: 1462
Дата рег-ции: Апр. 2013  


Помог: 91 раз(а)




Prizma пишет:
Что вы думаете о такой реализации

Думаю, что вы используете не подходящий инструмент.

1. При включении строгого режима получите ошибку из за попытки передать в качестве параметра строку похожую на число для int параметра.
2. Если тип извлекаемых данных имеет значение, установите опции PDO::ATTR_EMULATE_PREPARES и PDO::ATTR_STRINGIFY_FETCHES в false, остальное PHP сделает за вас.
3. Код конечно же ужасен, но видно, чего вы хотите. В принципе LIME сказал чего гуглить. Но сходу все эти солиды и мапперы будет сложно понять. Ниже приведу более простую реализацию, для новичка в таких делах вполне сойдет.

PHP:
скопировать код в буфер обмена
  1.  
  2. $dbh = new PDO('mysql:host=localhost;dbname=shop', 'username', 'password', [
  3.     PDO::ATTR_EMULATE_PREPARES  => false,
  4.     PDO::ATTR_STRINGIFY_FETCHES => false
  5. ]);
  6.  
  7. class Product
  8. {
  9.     private $name, $price;
  10.        
  11.     public function __set($property, $value)
  12.     {
  13.         $this->$property = $value;
  14.     }
  15.        
  16.     public function getName() : string
  17.     {
  18.         return $this->name;
  19.     }
  20.        
  21.     public function getPrice() : int
  22.     {
  23.         return $this->price;
  24.     }
  25. }
  26.  
  27. $products = $dbh->query('SELECT name, price FROM products');
  28. $products->setFetchMode(PDO::FETCH_CLASS, 'Product');
  29. foreach ($products as $product) {
  30.     var_dump($product->getName(), $product->getPrice());
  31. }
  32.  

Это просто пример, думаю идея понятна и сможете подпилить под свои нужды.

(Отредактировано автором: 22 Апреля, 2016 - 20:06:12)

 
 Top
Prizma
Отправлено: 22 Апреля, 2016 - 20:01:22
Post Id



Посетитель


Покинул форум
Сообщений всего: 463
Дата рег-ции: Июнь 2012  
Откуда: Санкт-Петербург


Помог: 5 раз(а)




LIME пишет:
И какая выгода использовать typehint?
LIME пишет:
Typehint надо использовать в классах использующих эти данные
Там будет уместно

логично, тогда и вправду не нужно приводить тип
LIME пишет:
А еще есть extract()

IDE будет ругаться, если бд не прикручена
LIME пишет:
Лучше завести класс работающий с хранилищем
Тогда для смены хранилища не придется менять класс модели
Принцип open/close SOLID

а вот тут можно поподробнее.
Не совсем понял, чем плох этот класс в роли хранилища?
(Добавление)
teddy пишет:
1. При включении строгого режима получите ошибку из за попытки передать в качестве параметра строку похожую на число для int параметра.

у меня включен строгий режим, нет ошибок.
teddy пишет:
Ниже приведу более простую реализацию, для новичка в таких делах вполне сойдет.
Я несколько лет занимаюсь php, могу сказать, что у меня не было наставника и я изучал язык сам и чувствую свои пробелы. Но реализация в примере меня не как не устраивает.

Я хочу, чтобы экземпляр класса Product было невозможно создать, если продукт в базе не найден. Класс реализует метод для проверки существования такого продукта, которым и нужно воспользоваться перед созданием экземпляра класса. Если продукта не существует в базе, будет фатальная ошибка, а следовательно где то в коде ошибка, которую будет легко найти и исправить.

Пока писал понял, что для выборки 100 продуктов в моем случае надо будет сделать 100 запросов, а так один... это не есть гуд. Так же много полей не будут использованы, т.е. лишняя нагрузка на память - ок согласен, надо что-то менять.

Просто проект не из одного файла состоит и хочется сделать универсальный класс под хранение информации об одном объекте
 
My status
 Top
teddy
Отправлено: 22 Апреля, 2016 - 20:27:04
Post Id


Участник


Покинул форум
Сообщений всего: 1462
Дата рег-ции: Апр. 2013  


Помог: 91 раз(а)




Prizma пишет:
у меня включен строгий режим, нет ошибок.

А должны быть. Может все таки не включен? Или у нас разные понятия о строгом режиме...

Prizma пишет:
Я хочу, чтобы экземпляр класса Product было невозможно создать, если продукт в базе не найден

1. Если есть класс с доступным конструктором как у вас, возможность создать экземпляр не пропадет.

2. У вас метод issetProduct не статичный, соответственно экземпляр вы создаете в любом случае, лишь с той разницей, что бросаете исключение если продукт не нашелся в БД. Не нужно себя обманывать. Кроме того, Product никак не должен брать на себя за подобную ответственность. Ничего не мешает сделать проверку по идентификатору вне Product и там же определить, надо ли создавать экземпляр.

(Отредактировано автором: 22 Апреля, 2016 - 20:27:37)

 
 Top
Prizma
Отправлено: 22 Апреля, 2016 - 20:29:23
Post Id



Посетитель


Покинул форум
Сообщений всего: 463
Дата рег-ции: Июнь 2012  
Откуда: Санкт-Петербург


Помог: 5 раз(а)




teddy пишет:
А должны быть. Может все таки не включен? Или у нас разные понятия о строгом режиме...

PHP:
скопировать код в буфер обмена
  1. error_reporting(E_STRICT);
  2. ini_set('error_reporting', E_ALL);
  3. ini_set('display_errors', 1);
  4. ini_set('display_startup_errors', 1);

этого мало? нет ошибок могу архив с бд и кодом залить.. экземпляр создается, все параметры доступны, ошибок нет
(Добавление)
teddy пишет:
У вас метод issetProduct не статичный, соответственно экземпляр вы создаете в любом случае, лишь с той разницей, что бросаете исключение если продукт не нашелся в БД. Не нужно себя обманывать. Кроме того, Product никак не должен брать на себя за подобную ответственность. Ничего не мешает сделать проверку по идентификатору вне Product и там же определить, надо ли создавать экземпляр.
Это опечатка, естественно я имел ввиду, что он будет статичный.
(Добавление)
Prizma пишет:
Это опечатка, естественно я имел ввиду, что он будет статичный.
поправил
(Добавление)
добавил код трейта Mysql, может еще какие ошибки увидите))
 
My status
 Top
teddy
Отправлено: 22 Апреля, 2016 - 20:49:19
Post Id


Участник


Покинул форум
Сообщений всего: 1462
Дата рег-ции: Апр. 2013  


Помог: 91 раз(а)




Prizma пишет:
этого мало?


В самом начале исполнения.

Prizma пишет:
Это опечатка, естественно я имел ввиду, что он будет статичный.

Ну и что Улыбка Смотрите. У вас так получается 2 запроса. Один с проверкой на существование, другой для получения продукта если он существует. Почему не выберете сразу продукт, вне класса Product, и если результатом будет не false, тогда работать дальше? Присмотритесь к примеру выше.

Prizma пишет:
добавил код трейта Mysql, может еще какие ошибки увидите))

Ну как минимум 3 косяка.
1. Отсутствие возможности определения параметров подключения динамически (нужно передавать их из вне)
2. Синглтон... Статика... жесткие связи в коде, сложно тестируемый код
3. Трейты нельзя протестировать как отдельное целое

Вообщем говорить об ошибках в данном случае можно не мало, если хотите понять ООП, изучайте фреймворки, читайте сурьезные статьи на эту тему Улыбка Как вариант можете попробовать с S.O.L.I.D как было предложено выше. Из ФВ рекомендую Zend Framework 2

Но придется попотеть))

(Отредактировано автором: 22 Апреля, 2016 - 20:50:26)

 
 Top
Мелкий Супермодератор
Отправлено: 22 Апреля, 2016 - 20:57:38
Post Id



Активный участник


Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 618 раз(а)




teddy пишет:
В самом начале исполнения.

В каждом (каждом!) PHP-файле.
http://php.net/manual/en/functio...claration.strict
Цитата:
It is possible to enable strict mode on a per-file basis.


-----
PostgreSQL DBA
 
 Top
Prizma
Отправлено: 22 Апреля, 2016 - 21:17:30
Post Id



Посетитель


Покинул форум
Сообщений всего: 463
Дата рег-ции: Июнь 2012  
Откуда: Санкт-Петербург


Помог: 5 раз(а)




Мелкий пишет:
В каждом (каждом!) PHP-файле.

в каждом вызываемом Вы имеете ввиду или в файле каждого класса который инклюдится тоже?
(Добавление)
По поводу SOLID нашел прекрасную статью на хабре https://habrahabr[dot]ru/post/208442/ там конечно в кратце, но начальное понимание дает... пойду читать, искать материал. Подумаю над лучшей реализацией.

Спасибо за советы, если будут еще комментарии обязательно прочитаю.
 
My status
 Top
LIME
Отправлено: 23 Апреля, 2016 - 02:19:44
Post Id


Активный участник


Покинул форум
Сообщений всего: 10732
Дата рег-ции: Нояб. 2010  


Помог: 322 раз(а)




teddy эээм... можно поспорить
Создается экземпляр при отсутствии данных? Как бэ да но какбэ нет смещения данных и значит н кушается память
Магия это не мое
Все эти геты и сеты не более чем латание дыр
Запрос в клиентском коде? Ну для примера наверное...
 
 Top
teddy
Отправлено: 23 Апреля, 2016 - 12:03:49
Post Id


Участник


Покинул форум
Сообщений всего: 1462
Дата рег-ции: Апр. 2013  


Помог: 91 раз(а)




Мелкий пишет:
В каждом (каждом!) PHP-файле.

Ну, это не всегда обязательно. В некоторых случаях достаточно определить строгий тип только в том файле, где инклюдится другой. Зависит от стека вызовов.

LIME пишет:
Как бэ да но какбэ нет смещения данных и значит н кушается память

Не, я не про экономию памяти говорил а лишь обратил внимание на тот факт, что экземпляр создается в любом случае, об этом свидетельствует вызов конструктора.

LIME пишет:
Все эти геты и сеты не более чем латание дыр

Почему латание дыр? В данном случае PDO пытается присвоить значения публичным свойствам одноименным с полями в БД соответствующие значения потому что был использован FETCH_CLASS. Когда он это делает, не находит такие свойства т.к они приватные и вызывается __set. Сами мы явно магию никак не используем. Не сторонник публичных свойств, но их любителям __set можно и не писать, PDO сам все сделает.

LIME пишет:
Запрос в клиентском коде? Ну для примера наверное...

Ну да, это был пример того, что хочет ТС, в более простой форме.
 
 Top
LIME
Отправлено: 23 Апреля, 2016 - 12:11:55
Post Id


Активный участник


Покинул форум
Сообщений всего: 10732
Дата рег-ции: Нояб. 2010  


Помог: 322 раз(а)




teddy я немного о другом
Из модели должны торчать только методы предметной области
Никаких сет
Тогда и магию не надо дергать
Лучше описать и знать что все под контролем
сетИмя плохо
ченджИмя хорошо
Потому как если объект может менять имя то мы это заложили
Кажется мелочь...но если подумать...
(Добавление)
teddy экземпляр это просто данные по смещению
Код всех экземпляров один
Просто к разным данным обращается
По разному смещению
 
 Top
teddy
Отправлено: 23 Апреля, 2016 - 12:17:37
Post Id


Участник


Покинул форум
Сообщений всего: 1462
Дата рег-ции: Апр. 2013  


Помог: 91 раз(а)




LIME
А где ты здесь увидел модель? Улыбка В данном случае экземпляр класса Product сущность, представляющая определенный тип в коде. У продукта есть название, цена и что то ещё. Этот продукт или набор продуктов в виде итератора/массива уже возвращает модель(возможно, какая нибудь ORM) как результирующий набор.

(Отредактировано автором: 23 Апреля, 2016 - 12:19:20)

 
 Top
LIME
Отправлено: 23 Апреля, 2016 - 12:23:49
Post Id


Активный участник


Покинул форум
Сообщений всего: 10732
Дата рег-ции: Нояб. 2010  


Помог: 322 раз(а)




Это именно модель
Она описывает предмет
И методы должны быть только те которые предусматривает этот предмет
Сетеры это лишнее знание для клиентского кода
А вообще я не о модели а о ооп в целом
(Добавление)
Я высказался
Ушел
 
 Top
teddy
Отправлено: 23 Апреля, 2016 - 12:36:33
Post Id


Участник


Покинул форум
Сообщений всего: 1462
Дата рег-ции: Апр. 2013  


Помог: 91 раз(а)




LIME
Может мы по разному позиционируем этот вопрос. На мой взгляд это не модель, а то, что возвращает модель, то есть результат работы модели. Например, $productsModel->getList($offset, $limit), вернет какой то результат, если данные нашлись. А в какой форме она вернет результат, например в виде массива, объекта/набора объектов или что то ещё уже дело десятое и это решает разработчик в зависимости от того, что ему нужно и удобно. Конечный результат работы модели не является моделью, не является тем самым комбайном, который обращается в определенное хранилище за данными. Имхо... Как то так Подмигивание

(Отредактировано автором: 23 Апреля, 2016 - 12:38:10)

 
 Top
Страниц (2): [1] 2 »
Сейчас эту тему просматривают: 0 (гостей: 0, зарегистрированных: 0)
« Объектно-ориентированное программирование »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB