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
Форумы портала PHP.SU :: Версия для печати :: PHP 7 Использование контроля скалярных типов
Форумы портала PHP.SU » » Объектно-ориентированное программирование » PHP 7 Использование контроля скалярных типов

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

1. Prizma - 22 Апреля, 2016 - 17:01:21 - перейти к сообщению
И так есть класс 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.  

Что вы думаете о такой реализации, уважаемые профи?
2. LIME - 22 Апреля, 2016 - 17:16:25 - перейти к сообщению
С точки зрения ооп все криво
И какая выгода использовать typehint?
Можно явно привести в list
А еще есть extract()
Лучше завести класс работающий с хранилищем
Тогда для смены хранилища не придется менять класс модели
Принцип open/close SOLID
(Добавление)
Typehint надо использовать в классах использующих эти данные
Там будет уместно
(Добавление)
Datamapper... ознакомся...
3. teddy - 22 Апреля, 2016 - 19:52:47 - перейти к сообщению
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.  

Это просто пример, думаю идея понятна и сможете подпилить под свои нужды.
4. Prizma - 22 Апреля, 2016 - 20:01:22 - перейти к сообщению
LIME пишет:
И какая выгода использовать typehint?
LIME пишет:
Typehint надо использовать в классах использующих эти данные
Там будет уместно

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

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

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

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

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

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

Просто проект не из одного файла состоит и хочется сделать универсальный класс под хранение информации об одном объекте
5. teddy - 22 Апреля, 2016 - 20:27:04 - перейти к сообщению
Prizma пишет:
у меня включен строгий режим, нет ошибок.

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

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

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

2. У вас метод issetProduct не статичный, соответственно экземпляр вы создаете в любом случае, лишь с той разницей, что бросаете исключение если продукт не нашелся в БД. Не нужно себя обманывать. Кроме того, Product никак не должен брать на себя за подобную ответственность. Ничего не мешает сделать проверку по идентификатору вне Product и там же определить, надо ли создавать экземпляр.
6. Prizma - 22 Апреля, 2016 - 20:29:23 - перейти к сообщению
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, может еще какие ошибки увидите))
7. teddy - 22 Апреля, 2016 - 20:49:19 - перейти к сообщению
Prizma пишет:
этого мало?


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

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

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

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

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

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

Но придется попотеть))
8. Мелкий - 22 Апреля, 2016 - 20:57:38 - перейти к сообщению
teddy пишет:
В самом начале исполнения.

В каждом (каждом!) PHP-файле.
http://php.net/manual/en/functio...claration.strict
Цитата:
It is possible to enable strict mode on a per-file basis.
9. Prizma - 22 Апреля, 2016 - 21:17:30 - перейти к сообщению
Мелкий пишет:
В каждом (каждом!) PHP-файле.

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

Спасибо за советы, если будут еще комментарии обязательно прочитаю.
10. LIME - 23 Апреля, 2016 - 02:19:44 - перейти к сообщению
teddy эээм... можно поспорить
Создается экземпляр при отсутствии данных? Как бэ да но какбэ нет смещения данных и значит н кушается память
Магия это не мое
Все эти геты и сеты не более чем латание дыр
Запрос в клиентском коде? Ну для примера наверное...
11. teddy - 23 Апреля, 2016 - 12:03:49 - перейти к сообщению
Мелкий пишет:
В каждом (каждом!) PHP-файле.

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

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

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

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

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

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

Ну да, это был пример того, что хочет ТС, в более простой форме.
12. LIME - 23 Апреля, 2016 - 12:11:55 - перейти к сообщению
teddy я немного о другом
Из модели должны торчать только методы предметной области
Никаких сет
Тогда и магию не надо дергать
Лучше описать и знать что все под контролем
сетИмя плохо
ченджИмя хорошо
Потому как если объект может менять имя то мы это заложили
Кажется мелочь...но если подумать...
(Добавление)
teddy экземпляр это просто данные по смещению
Код всех экземпляров один
Просто к разным данным обращается
По разному смещению
13. teddy - 23 Апреля, 2016 - 12:17:37 - перейти к сообщению
LIME
А где ты здесь увидел модель? Улыбка В данном случае экземпляр класса Product сущность, представляющая определенный тип в коде. У продукта есть название, цена и что то ещё. Этот продукт или набор продуктов в виде итератора/массива уже возвращает модель(возможно, какая нибудь ORM) как результирующий набор.
14. LIME - 23 Апреля, 2016 - 12:23:49 - перейти к сообщению
Это именно модель
Она описывает предмет
И методы должны быть только те которые предусматривает этот предмет
Сетеры это лишнее знание для клиентского кода
А вообще я не о модели а о ооп в целом
(Добавление)
Я высказался
Ушел
15. teddy - 23 Апреля, 2016 - 12:36:33 - перейти к сообщению
LIME
Может мы по разному позиционируем этот вопрос. На мой взгляд это не модель, а то, что возвращает модель, то есть результат работы модели. Например, $productsModel->getList($offset, $limit), вернет какой то результат, если данные нашлись. А в какой форме она вернет результат, например в виде массива, объекта/набора объектов или что то ещё уже дело десятое и это решает разработчик в зависимости от того, что ему нужно и удобно. Конечный результат работы модели не является моделью, не является тем самым комбайном, который обращается в определенное хранилище за данными. Имхо... Как то так Подмигивание

 

Powered by ExBB FM 1.0 RC1