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

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

16. Prizma - 24 Апреля, 2016 - 00:18:44 - перейти к сообщению
Интересные вы обсуждения ведёте, однако магические методы точно использовать не хорошо..
teddy пишет:
$products = $dbh->query('SELECT name, price FROM products');
$products->setFetchMode(PDO::FETCH_CLASS, 'Product');
Этот код не полный там не хватает наверно execute... чтобы он работал
Однако всё равно не думаю, что стоит пользоваться этим механизмом PDO, если он требует public переменные или __set ... начал читать одну из книжек по ООП "Быстрая разработка программ" от Роберта Мартина, пошарил по интернету...

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

PS: не думал, что может быть так сложно правильно реализовать достаточно простой класс, следуя всем принципам ООП. Однако, почитав немного поглубже про принципы, шаблоны... понял, что моё понимание было достаточно узким и не полным относительно этих принципов и ООП в целом.
17. LIME - 24 Апреля, 2016 - 00:23:18 - перейти к сообщению
Ниче не читал все пофиг
Увидел что догуглил до дядюшки Мартина
Дядя Мартин крут
И его быстрая разработка лучшее
Prizma топаешь в нужном направлении... так держать
Еще grasp
Если дядюшку Мартина асилишь
(Добавление)
Prizmaтеди не слушай
Он ошибается
18. teddy - 24 Апреля, 2016 - 01:28:02 - перейти к сообщению
Prizma пишет:
однако магические методы точно использовать не хорошо..

Это кто сказал? Причины в студию, пруф, что это всегда зло. Нужно использовать, но с умом. А вообще выстрелить в ногу можно всякими инструментами, даже полезными, если разработчик плохо понимает с чем имеет дело.

Prizma пишет:
Этот код не полный там не хватает наверно execute... чтобы он работал

Конкретно в этом примере execute не нужен.

LIME пишет:
теди не слушай
Он ошибается

Я прекрасно понимаю причины твоих возражений касательно __set и отчасти ты прав, НО:

По поводу "Сетеры это лишнее знание для клиентского кода", в данном случае это очень удобно. Но да, остается возможность переопределить значение "прямым" обращением, инкапсуляция и все такое... Эту возможность можно "прикрыть" простой проверкой в __set.
Если исходить из логики, что __set предназначен для PDO, можно проверить, если свойство пустое, то пускать на запись, значит пишет PDO а не кто то в клиентском коде. Но если значение конкретному свойству уже присвоено, бросать исключение, значит это пытается сделать не PDO. Все просто. И простоту реализации сохранили, и такие знания у клиентского кода отняли.
19. Prizma - 24 Апреля, 2016 - 11:55:19 - перейти к сообщению
teddy пишет:
Это кто сказал? Причины в студию, пруф, что это всегда зло. Нужно использовать, но с умом. А вообще выстрелить в ногу можно всякими инструментами, даже полезными, если разработчик плохо понимает с чем имеет дело.

teddy пишет:
По поводу "Сетеры это лишнее знание для клиентского кода", в данном случае это очень удобно. Но да, остается возможность переопределить значение "прямым" обращением, инкапсуляция и все такое... Эту возможность можно "прикрыть" простой проверкой в __set.

Причин много даже для меня:
1. Код становится менее понятным и в случае нагруженности set, менее предсказуемым
2. IDE не сможет тебе помогать (не в данном случае, тут переменные будут браться через getVar), однако если определять их в не PDO, то будет ругаться, на их область видимости.
3. Любые изменения названия полей в бд, вытекут в сложно диагностируемую ошибку, можно конечно добавить в __set проверку, что если не существует генерировать предупреждение, но зачем создавать такую почву?
4. Есть другие способы более элегантные, по реализации.
5. Сколько я в своей практике не использовал __set рано или поздно приходил, к тому, что эту часть кода приходилось переписывать, либо постоянно модифицировать.
LIME пишет:
Prizma топаешь в нужном направлении... так держать
Еще grasp
Если дядюшку Мартина асилишь
книжка интересная, по GRASP надеюсь, тоже в ближайшее время доберусь ...

Спасибо за советы.
20. teddy - 24 Апреля, 2016 - 12:53:50 - перейти к сообщению
Prizma пишет:
Есть другие способы более элегантные, по реализации.

Есть, но их объяснение может привести к нескольким простыням текста и столько же текста для того что бы ответить на ваши вопросы. Если бы вы их знали не задавали такие вопросы, по этому не нужно ля-ля... Улыбка
Prizma пишет:
Код становится менее понятным и в случае нагруженности set, менее предсказуемым

Как уже говорил, в данном случае мы лишь помогаем PDO сделать свою работу. Такая реализация была предложена исходя из логики, что вы делаете проект, а изучать подробности "академической" реализации это долго. И это решение в 100 раз лучше чем то с чем вы пришли на форум создавая эту тему
Prizma пишет:
Сколько я в своей практике не использовал __set рано или поздно приходил, к тому, что эту часть кода приходилось переписывать, либо постоянно модифицировать.

А я вот не помню что бы мне приходилось это делать. Наверное не там применяли
Prizma пишет:
IDE не сможет тебе помогать

Радость

Вот LIME увидел реальную проблему, она есть и это не единственная проблема если докопаться с точки зрения ООП.
Об этом много писать. Не хочу, правда. Опять же, если не понятна суть примера, см ещё раз ответ на вторую цитату в этом посте...
21. LIME - 24 Апреля, 2016 - 13:41:58 - перейти к сообщению
Надо добавить что сетеры уместны там где установка значения это есть предметная область класса
То есть класс нужен как хранилище и только
Я говорил о любых сетерах не только магическких
22. Prizma - 24 Апреля, 2016 - 17:01:14 - перейти к сообщению
Я определился, что мне нужно. Мне нужен класс Product как хранилище... я решил разделить его на 3 класса:
ProductBreifInfo (содержит только 2 поля - имя и id)
ProductGeneralInfo extends ProductBreifInfo (добавляются дополнительные поля)
ProductFullIngo extends ProductGeneralInfo (добавляются редко используемые тяжелые текстовые поля)
не уверен стоило ли делать так или просто оставлять не сообщенные поля пустыми.

По реализации заполнения классов у меня появилось 2 варианта
Вариант 1 (на примере первого класса):
PHP:
скопировать код в буфер обмена
  1. class ProductBreifInfo
  2. {
  3.     /* @var int */
  4.     private $id;
  5.     /* @var string */
  6.     private $name;
  7.  
  8.     public function getId() : int
  9.     {
  10.         return $this->id;
  11.     }
  12.    
  13.     public function setId(int $id) : void
  14.     {
  15.         $this->id = $id;
  16.     }
  17.  
  18.     public function getName() : string
  19.     {
  20.         return $this->name;
  21.     }
  22.  
  23.     public function setName(string $name) : void
  24.     {
  25.         $this->name = $name;
  26.     }
  27. }
  28.  
  29. $products = [];
  30. $q = $db->prepare('SELECT id, name FROM products');
  31. $q->execute();
  32. while($r = $q->fetch(PDO::FETCH_ASSOC)) {
  33.     $product = new ProductBreifInfo();
  34.     $product->setId($r['id']);
  35.     $product->setName($r['name']);
  36.     $products[$product->getId()] = $product;
  37. }

Вариант 2 (на примере первого класса):
PHP:
скопировать код в буфер обмена
  1. class ProductBreifInfo
  2. {
  3.     /* @var int */
  4.     private $id;
  5.     /* @var string */
  6.     private $name;
  7.    
  8.     public function __construct($source)
  9.     {
  10.         foreach ($source as $v => $k) {
  11.             if(!isset($this->$k)) {
  12.                 throw new \InvalidArgumentException();
  13.             }
  14.             $this->$k = $v;
  15.         }
  16.     }
  17.  
  18.     public function getId() : int
  19.     {
  20.         return $this->id;
  21.     }
  22.  
  23.     public function getName() : string
  24.     {
  25.         return $this->name;
  26.     }
  27. }
  28.  
  29. $products = [];
  30. $q = $db->prepare('SELECT id, name FROM products');
  31. $q->execute();
  32. while($r = $q->fetchObject("ProductBreifInfo")) {
  33.     $product = new ProductBreifInfo($r);
  34.     $products[$product->getId()] = $product;
  35. }

Первый вариант мне кажется топорным. Второй вариант мне нравится больше, только, тогда вопрос - а стоит ли разделять класс Product - пусть есть пустые поля, они же не мешают, всегда если надо можно добавить недостающие в старый экземпляр, а не грузить всё заново.
23. teddy - 24 Апреля, 2016 - 17:16:39 - перейти к сообщению
LIME
Приблизительно об этом я и говорил, о классе, экземпляры которого представляют определенный тип связанный с данными, которые подвергаются CRUD-у.
Может класс на предыдущей страничке был немного скудным, поэтому появились вопросы. Вот более ясный пример использования

PHP:
скопировать код в буфер обмена
  1. //пример использования #1
  2. $products = $storage->getList();//получили список объектов типа Product
  3. //пример #2. insert и update
  4. $product->setName($name);
  5. $product->setPrice($price);
  6. $storage->save($product);


Таким образом получаем унифицированный интерфейс обращения с продуктом и наличие соответствующего типа в коде а так же возможность локализировать некоторые стандартные вычисления(если они есть) в методах этого типа вместо того что бы дублировать их везде где используется product
(Добавление)
Prizma пишет:
По реализации заполнения классов у меня появилось 2 варианта

Так у вас будет 2 цикла. Один заполнение массива, другой - для вывода данных из этого массива(через объекты которые в этом массиве). Тут явно напрашивается Traversable.
24. LIME - 24 Апреля, 2016 - 17:27:24 - перейти к сообщению
никаких set/save
публичный интерфейс должен содержать только методы действия с моделью
25. Prizma - 24 Апреля, 2016 - 18:11:37 - перейти к сообщению
LIME пишет:
Просто если таких параметров 15-20, окей пилю в аргумент массив или объект, получается массив полученный от $q->fetch(...) идет в аргумент, а чем статический create будет лучше стандартного __construct($name, $price)?
teddy пишет:
Так у вас будет 2 цикла. Один заполнение массива, другой - для вывода данных из этого массива(через объекты которые в этом массиве). Тут явно напрашивается Traversable.
Согласен - расточительно. Почитал про итераторы.. т.е. реализовать класс class productList implements Iterator для этого дела? и помимо обязательных методов описанных интерфейсом, методы добавление удаления типа addItem, removeItem так?
26. LIME - 24 Апреля, 2016 - 18:18:43 - перейти к сообщению
Да я вообще не об этом
Считай выше был просто псевдокод
Я о том что если с предметом надо совершать действие создать/приготовить/перекрасить то должны быть эти методы
А всякие сетеры гереты и сэйвы это детали реализации которые надо инкапсулировать
Думаю теди понял а ты просто запомни потом поймешь
27. teddy - 25 Апреля, 2016 - 00:29:53 - перейти к сообщению
Prizma пишет:
т.е. реализовать класс class productList implements Iterator для этого дела?

В вашем случае проще всего использовать PDOStatement который и так Traversable с соответствующим типом fetch-а. Если создаете и заполняете экземпляр самостоятельно самый простой вариант это задействовать Generator
28. Prizma - 25 Апреля, 2016 - 00:31:10 - перейти к сообщению
И так провел воскресенье, вдаваясь в тонкости ООП и разбирая несколько примеров, и вот, что получилось:
PHP:
скопировать код в буфер обмена
  1. interface IProductsSource
  2. {
  3.     const LOAD_BRIEF_INFO = 1;
  4.     const LOAD_GENERAL_INFO = 2;
  5.     const LOAD_FULL_INFO = 3;
  6.  
  7.     /**
  8.      * @param array $productsId
  9.      * @param int $mode
  10.      * @return array [id=>Product]
  11.      */
  12.     public function load(array $productsId, int $mode) : array;
  13.  
  14.     public function update(Product $product) : bool;
  15.  
  16.     public function delete(Product $product) : bool;
  17. }
  18.  
  19.  
  20. class MysqlIProductsSource implements IProductsSource
  21. {
  22.     use Mysql;
  23.    
  24.     public function load(array $productsId, int $mode) : array
  25.     {
  26.         if(empty($productsId)) {
  27.             return [];
  28.         }
  29.         foreach ($productsId as &$id) {
  30.             if (is_string($id)) {
  31.                 $id *= 1;
  32.             } elseif (!is_int($id)) {
  33.                 throw new \InvalidArgumentException('$productsId');
  34.             }
  35.         }
  36.         $in = implode(',', $productsId);
  37.         $fields = [];
  38.         switch($mode) {
  39.             case self::LOAD_FULL_INFO:
  40.                 $fields[] = 'description';
  41.                 // no break
  42.             case self::LOAD_GENERAL_INFO:
  43.                 $fields[] = 'producer_id, author_id, currency_id, price, in_stock_amount, delivery_days, short_desc,'.
  44.                     'seo_desc, seo_kw, created, modified, obj_properties';
  45.                 // no break
  46.             case self::LOAD_BRIEF_INFO:
  47.                 $fields[] = 'id, name';
  48.                 break;
  49.             default:
  50.                 throw new \InvalidArgumentException('$mode');
  51.         }
  52.         $fields = implode(',', $fields);
  53.         $sql = 'SELECT '.$fields.' FROM '.MTables::PRODUCTS.' WHERE id IN('.$in.');';
  54.         $re = [];
  55.         $q = self::db()->prepare($sql);
  56.         $q->execute();
  57.         while($r = $q->fetch(\PDO::FETCH_ASSOC)) {
  58.             $re[$r['id']] = new Product($r);
  59.         }
  60.         return $re;
  61.     }
  62.  
  63.     public function update(Product $product) : bool
  64.     {
  65.         // TODO: Implement update() method.
  66.         return false;
  67.     }
  68.  
  69.     public function delete(Product $product) : bool
  70.     {
  71.         // TODO: Implement delete() method.
  72.         return false;
  73.     }
  74.  
  75. }
  76.  
  77.  
  78. class ProductRepository
  79. {
  80.     /** @var IProductsSource */
  81.     private $source;
  82.  
  83.     public function __construct(IProductsSource $source)
  84.     {
  85.         $this->setSource($source);
  86.     }
  87.  
  88.     public function setSource(IProductsSource $source)
  89.     {
  90.         $this->source = $source;
  91.     }
  92.    
  93.     /**
  94.      * @param array $productsId
  95.      * @param int $mode
  96.      * @return array [id=>Product]
  97.      */
  98.     public function load(array $productsId, int $mode) : array
  99.     {
  100.         return $this->source->load($productsId, $mode);
  101.     }
  102.  
  103.     public function update(Product $product) : bool
  104.     {
  105.         return $this->source->update($product);
  106.     }
  107.  
  108.     public function delete(Product $product) : bool
  109.     {
  110.         return $this->source->delete($product);
  111.     }
  112. }
  113.  
  114.  
  115. class Product
  116. {
  117.     /* @var int */
  118.     private $id;
  119.     /* @var int */
  120.     private $producerId;
  121.     /* ... */
  122.  
  123.     public function __construct(array $source)
  124.     {
  125.         foreach ($source as $key => $value) {
  126.             $this->$key = $value;
  127.         }
  128.     }
  129.        
  130.     public function getId() : int {/* ... */}
  131.     /* and another getters... */
  132. }
  133.  
  134.  
  135. /* EXAMPLE */
  136.  
  137. $re = new ProductRepository(new MysqlIProductsSource());
  138. $products = $re->load([1, 2, 46, 58], IProductsSource::LOAD_GENERAL_INFO);
  139. foreach ($products as $product) {
  140.     echo $product->getName();
  141. }

Как то так.. Для хранения экземпляров класса Product ничего лучше массива не приудмал, хотя была мысль сделать ProductList, даже написал его, но в итоге не понял в чем принципиальная разница от простого массива... разве, что добавить туда методы updateAll, deleteAll которые мне пока точно не нужны. Хотя смотря в будущее возможно всё таки к этому еще вернусь, когда буду переписывать корзину товаров, там и общая стоимость всех товаров и скидка в от суммы заказа тоже зависит...

Данные перед выходом нужно обработать (именно сами данные): перевести цены в рубли, расчитать кол-во дней до конца акции, пропарсить описание на предмет подстановки... (это те функции, которые уже реализованы на сайте, а я собственно решил переписать свой код 2-ух летней давности, со свежим взглядом). Если я правильно начинаю понимать ООП, это именно те методы, которые должны быть реализованы в классе Product?
(Добавление)
еще подумываю в геттерах класса Product генерировать ошибку в случае запроса атрибута со значением равным null (т.е. те которые не определены в выбранном режиме).
29. Bio man - 30 Апреля, 2016 - 19:01:31 - перейти к сообщению
интересное чтиво http://sergeyteplyakov[dot]blogspot[dot]ca/
ооп, солид, контракты, все что хочешь
так же много статей об ОО дизайне
и еще есть пару списков маст-рид книг, с кратким описанием, и подробные рецензии на некоторые книги
примеры в основном на C# но не думаю, что это будет проблемой

 

Powered by ExBB FM 1.0 RC1