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 Использование контроля скалярных типов [2]

 PHP.SU

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


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

> Описание: интересует мнение профессионалов
Prizma
Отправлено: 24 Апреля, 2016 - 00:18:44
Post Id



Посетитель


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


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




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

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

PS: не думал, что может быть так сложно правильно реализовать достаточно простой класс, следуя всем принципам ООП. Однако, почитав немного поглубже про принципы, шаблоны... понял, что моё понимание было достаточно узким и не полным относительно этих принципов и ООП в целом.
 
My status
 Top
LIME
Отправлено: 24 Апреля, 2016 - 00:23:18
Post Id


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


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


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




Ниче не читал все пофиг
Увидел что догуглил до дядюшки Мартина
Дядя Мартин крут
И его быстрая разработка лучшее
Prizma топаешь в нужном направлении... так держать
Еще grasp
Если дядюшку Мартина асилишь
(Добавление)
Prizmaтеди не слушай
Он ошибается
 
 Top
teddy
Отправлено: 24 Апреля, 2016 - 01:28:02
Post Id


Участник


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


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




Prizma пишет:
однако магические методы точно использовать не хорошо..

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

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

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

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

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

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

(Отредактировано автором: 24 Апреля, 2016 - 01:43:03)

 
 Top
Prizma
Отправлено: 24 Апреля, 2016 - 11:55:19
Post Id



Посетитель


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


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




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

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

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

Спасибо за советы.
 
My status
 Top
teddy
Отправлено: 24 Апреля, 2016 - 12:53:50
Post Id


Участник


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


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




Prizma пишет:
Есть другие способы более элегантные, по реализации.

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

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

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

Радость

Вот LIME увидел реальную проблему, она есть и это не единственная проблема если докопаться с точки зрения ООП.
Об этом много писать. Не хочу, правда. Опять же, если не понятна суть примера, см ещё раз ответ на вторую цитату в этом посте...

(Отредактировано автором: 24 Апреля, 2016 - 13:05:13)

 
 Top
LIME
Отправлено: 24 Апреля, 2016 - 13:41:58
Post Id


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


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


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




Надо добавить что сетеры уместны там где установка значения это есть предметная область класса
То есть класс нужен как хранилище и только
Я говорил о любых сетерах не только магическких
 
 Top
Prizma
Отправлено: 24 Апреля, 2016 - 17:01:14
Post Id



Посетитель


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


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




Я определился, что мне нужно. Мне нужен класс 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 - пусть есть пустые поля, они же не мешают, всегда если надо можно добавить недостающие в старый экземпляр, а не грузить всё заново.
 
My status
 Top
teddy
Отправлено: 24 Апреля, 2016 - 17:16:39
Post Id


Участник


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


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




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 Апреля, 2016 - 17:23:14)

 
 Top
LIME
Отправлено: 24 Апреля, 2016 - 17:27:24
Post Id


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


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


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




никаких set/save
публичный интерфейс должен содержать только методы действия с моделью
 
 Top
Prizma
Отправлено: 24 Апреля, 2016 - 18:11:37
Post Id



Посетитель


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


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




LIME пишет:
Просто если таких параметров 15-20, окей пилю в аргумент массив или объект, получается массив полученный от $q->fetch(...) идет в аргумент, а чем статический create будет лучше стандартного __construct($name, $price)?
teddy пишет:
Так у вас будет 2 цикла. Один заполнение массива, другой - для вывода данных из этого массива(через объекты которые в этом массиве). Тут явно напрашивается Traversable.
Согласен - расточительно. Почитал про итераторы.. т.е. реализовать класс class productList implements Iterator для этого дела? и помимо обязательных методов описанных интерфейсом, методы добавление удаления типа addItem, removeItem так?
 
My status
 Top
LIME
Отправлено: 24 Апреля, 2016 - 18:18:43
Post Id


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


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


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




Да я вообще не об этом
Считай выше был просто псевдокод
Я о том что если с предметом надо совершать действие создать/приготовить/перекрасить то должны быть эти методы
А всякие сетеры гереты и сэйвы это детали реализации которые надо инкапсулировать
Думаю теди понял а ты просто запомни потом поймешь
 
 Top
teddy
Отправлено: 25 Апреля, 2016 - 00:29:53
Post Id


Участник


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


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




Prizma пишет:
т.е. реализовать класс class productList implements Iterator для этого дела?

В вашем случае проще всего использовать PDOStatement который и так Traversable с соответствующим типом fetch-а. Если создаете и заполняете экземпляр самостоятельно самый простой вариант это задействовать Generator
 
 Top
Prizma
Отправлено: 25 Апреля, 2016 - 00:31:10
Post Id



Посетитель


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


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




И так провел воскресенье, вдаваясь в тонкости ООП и разбирая несколько примеров, и вот, что получилось:
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 (т.е. те которые не определены в выбранном режиме).

(Отредактировано автором: 25 Апреля, 2016 - 00:35:28)

 
My status
 Top
Bio man
Отправлено: 30 Апреля, 2016 - 19:01:31
Post Id


Постоянный участник


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


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




интересное чтиво http://sergeyteplyakov[dot]blogspot[dot]ca/
ооп, солид, контракты, все что хочешь
так же много статей об ОО дизайне
и еще есть пару списков маст-рид книг, с кратким описанием, и подробные рецензии на некоторые книги
примеры в основном на C# но не думаю, что это будет проблемой
 
 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