• В правильном ли направлении строится система роутингов?

    Falseclock
    @Falseclock
    dj_maestro: и кстати, если у вас рутинг для всех и для любого, то уберите public для переменных. Они же для каждого вызова уникальные раз и никто не должен их изменять - это два. Если вы не заимеете привычку правильно описывать переменные и классы, область видимости и доступ, то опять же можете нарваться на баги, которые с трудом отыскиваются.
  • В правильном ли направлении строится система роутингов?

    Falseclock
    @Falseclock
    dj_maestro: Вся правильно! Пишите свой велосипед. Я за 15 лет так и не пощупал что такое "Yii, в Symfony, в Silex " и без понятия. Как-то открыл исходники Worpdress и меня хватил удар.

    Все что вам нужно будет в разработке - это composer, через который вы будете подключать огромные классы, например работа с XML, XLS, БД, кэшем, демонами, сокетами и т.д и т.д., которыми пользуются десятки тысяч и 99% багов исправлены, а функционал достаточен для ваших нужд. В крайнем случае можно всегда сделать extend, но мне не приходилось. Тем более вы изобретаете не велосипед, а реализуете свой требуемый функционал. Изобрести велосипед - это если не пользоваться готовыми встроенными в PHP функциями и классами. Например сделать свою обертку с работой Memcached.
  • В правильном ли направлении строится система роутингов?

    Falseclock
    @Falseclock
    и еще из замечаний.

    __construct() - это только конструктор. Правилом хорошего тона будет только принимает переданные значения и только, но не обрабатывать и не модифицировать.

    Обработкой переданных данных должен заниматься какой-то метод.
    Или если нужно, внутри конструктора вызвать этот метод, что не желательно.

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

    Falseclock
    @Falseclock
    при этом ваш nameController должен имплементить интерфейс или расширять другой класс, в котором вы опишите как минимум две функции: render и getContent.

    render - вывод в браузер
    getContent - отдача контента другому классу

    interface View
    {
    	public function render($model = null);
    	public function content($model = null);
    }


    В подключаемом классе описываете что нужно вообще делать
    class Handler implements View {
    	public function content($module = null)
    	{
    		ob_start();
    		ob_end_clean();
                   // возврат значения
    	}
    
    	public function render($module = null)
    	{
                   // эхо вашего HTML
    	}
    }


    а потом уже в вашем самом классе

    class MainHandler extends Handler {
     // а тут реализуете что вам нужно.
    }
  • В правильном ли направлении строится система роутингов?

    Falseclock
    @Falseclock
    Ай маладец! Нравятся мне такие ответы. эхи можно заменить на throw new Exception("ашипка")
  • Как задать условие при первом клике?

    Falseclock
    @Falseclock
    расскажите про область видимости переменной i внутри функции
  • Как упростить перепор массива?

    Falseclock
    @Falseclock
    коммент корявый получился, так как сайт съедает скобки...
  • Как упростить перепор массива?

    Falseclock
    @Falseclock
    Это пздц... просто пздц... первое правило программирования - избавься от кода, который делает одно и тоже, вынеси в функцию. Создать SELECT на стороне клиента не занимает время и ресурсы. Не важно где сформирован код, если он большой - он будет тормозить всегда - сформирован ли он сервером или сформирован через JS.
  • Как упростить перепор массива?

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

    1. В PHP рисуете только сам , а в нем указываете дефолтное значение, например так вот:

    <select name="my select" data-default-value="какое-то значение"></select>


    2. далее в HTML рисуете JSON строку со всеми значениями

    SELECT_BUILD = {1:Знач1, 2:Знач2, и т.д.}

    3. В JS ставите обработчик как только страница загружена в котором находите все селекты, у которых есть атрибут data-default-value

    4. По циклу из SELECT_BUILD добавляете в этот селект

    5. Ставите дефолтное значение присваивая sele.value=<значение>

    Профит
  • Как отловить window.alert и отменить его?

    Falseclock
    @Falseclock
    mrKorg:
    window.alert = function(text) { console.log(text); }
  • Как сделать зеркальные поля при печати из браузера?

    Falseclock
    @Falseclock
    Sergey Goryachev: у автора спросите, он уже понял и реализует
  • Как сделать из таких get параметров объект в Javascript?

    Falseclock
    @Falseclock
    Дмитрий Беляев: каждый дрочит как он хочет. А передавать данные в GET запросах надо формализованно, а не в непонятных строках со скобками, амперсандами и прочим хламом, который может просто привести к неправильной обработке HTTP запроса.
  • Как сделать зеркальные поля при печати из браузера?

    Falseclock
    @Falseclock
    Sergey Goryachev: а при том, что если мы можем через CSS определить номер страницы, то что нам мешает делать разные margin для четных и нечетных страниц.
  • Как сделать зеркальные поля при печати из браузера?

    Falseclock
    @Falseclock
    а вот и не правда... средствами CSS можно определить номер страницы, а соответственно для печати определить и отступы для каждой из них

    Вот так вот можно например нумеровать страницы
    @page {
      @bottom-right {
        content: counter(page) " of " counter(pages);
      }
    }
  • Как сделать из таких get параметров объект в Javascript?

    Falseclock
    @Falseclock
    ааа.. вам наоборот надо..

    ну тоже самое, на стороне сервера кодируете данные, а на стороне JS парсите
  • Поможет ли знание алгоритмов в решении типичных задач?

    Falseclock
    @Falseclock
    А вот пример полной реализации данного алогоритма

    <?php
    
    class InvoiceHandler
    {
    	protected $db		= null;
    
    	public function __construct($invoice)
    	{
    		$this->db = DBPool::me()->getLink();
    
    		self::Handler($invoice);
    	}
    	//TODO: ставить ждет оплату если стоит в новом заказе
    	private function Handler($invoice)
    	{
    		$db = $this->db;
    		
    		$socket = array();
    		
    		$structure = Utils1c::prepareStructure($invoice,'invoice');
    		
    		// Усли инвойс поставлен на удаление, то и мы удалим инвойс из базы
    		if ($invoice['DeletionMark'] == true) {
    			
    			$socket = array_merge($socket, self::deleteInvoiceFromCRM($invoice,$structure));
    		
    		// Инвойс обновился или же создан новый. Необходимо проверить
    		} else {
    			// Делаем поиск в базе 
    			$sth = self::findInvoiceDataFromCRM($invoice);
    			
    			// 4.	У нас есть привязка счета к какому-то заказу
    			if ($sth->rows()) 
    			{
    				// Выгружаем данные полностью
    				$crmData = $sth->fetchrowset('item_uuid');
    
    				$structure['debug'][] = 'найдено ' . count($crmData) . ' записей в базе по данному счету';
    				
    				// 2.	Удалим записи из invoice_data если там есть лишние
    				$socket = array_merge($socket, self::deleteInvoiceDataOnDeleteInERP($invoice,$crmData,$structure));
    				
    				$structure['debug'][] = 'количество записей после уделания: ' . count($crmData);
    				
    				// 3.	Добавляем в invoice_data новые строки буз order_data_id, если их добавили через 1с
    				$socket = array_merge($socket, self::insertInvoiceDataOnNewRowInERP($invoice,$crmData,$structure));
    				
    				// 4.	Необходимо сравнить все позиции и обновить значения
    				$socket = array_merge($socket, self::compareInvoiceBetweenCRMandERP($invoice,$crmData,$structure));
    				
    			} else {
    				$structure['debug'][] = 'У нас нет данных в order_data_id по данному счету';
    			}
    		}
    
    		$socket[] = Driver1c::SOCKET_MARKER.json_encode($structure, JSON_UNESCAPED_UNICODE);
    		
    		new WebSocketClient($socket);
    		
    		return;
    	}
    	
    	private function deleteInvoiceFromCRM($invoice, &$structure)
    	{
    		$db = $this->db;
    		$socket = array();
    		
    		$structure['action'] = "delete";
    		
    		// Перед удалением проверить что у нас есть привязка и в случае наличи
    		// позиции заказов отметить как отмененные
    		$sth = $db->prepare("
    			DELETE FROM 
    				invoices 
    			USING 
    				invoice_data 
    			WHERE 
    				invoices.invoice_uuid = invoice_data.invoice_uuid AND 
    				invoices.invoice_uuid = ?
    			RETURNING 
    				invoice_data.order_data_id,
    				invoices.order_id
    		");
    		$sth->execute($invoice['Ref']);
    		
    		// Если инвойс сохраненный через CRM
    		if ( $sth->rows() ) {
    			$std = $db->prepare("UPDATE order_data SET status = ? WHERE order_data_id = ?");
    			while ($row = $sth->fetchrow()) {
    				$structure['order_id'] = $row['order_id'];
    				$structure['order_data'][] = array ($row['order_data_id'] => 'delete');
    				$std->execute(OrderState::CANCELED,$row['order_data_id']);
    				$socket[] = "Motion:change:".$row['order_data_id'].":".OrderState::CANCELED.":Status";
    			}
    		}
    		
    		return $socket;
    	}
    	
    	private function findInvoiceDataFromCRM($invoice)
    	{
    		$db = $this->db;
    		
    		// Нам в первую очередь надо найти сам заказ и что инвойс к нашему заказу привязан
    		// поэтому джоиним инвойсы через номер заказа по uuid счета
    		// при этом также вытаскиваем то, что у нас должно или не должно быть в счете
    		$sth = $db->prepare
    		("
    			SELECT 
    				i.invoice_number,
    				i.invoice_amount,
    				i.invoice_customer,
    				i.invoice_date,
    				timestamp ? AS date_check,
    				o.order_id,
    				id.order_data_id,
    				id.quantity,
    				id.price,
    				id.item_uuid
    			FROM
    				orders o
    			JOIN invoices i ON
    				i.order_id = o.order_id
    			JOIN invoice_data id ON
    				id.invoice_uuid = i.invoice_uuid
    			WHERE
    				i.invoice_uuid = ?
    		");
    		$sth->execute($invoice['Date'], $invoice['Ref']);
    		
    		/* пример выборки
    		 invoice_number | invoice_amount |           invoice_customer           |    invoice_date     |    date_check    | order_id | order_data_id | quantity | price |              item_uuid
    		----------------+----------------+--------------------------------------+---------------------+------------------+----------+---------------+----------+-------+--------------------------------------
    		 00000000073    |         493920 | bd63f5ba-55f1-11e5-5d98-0e459e882122 | 2017-03-02 01:51:35 |                  |     5110 |         10125 |        8 | 41160 | 76e80df8-5802-11e4-6686-0e459e882122
    		 00000000073    |         493920 | bd63f5ba-55f1-11e5-5d98-0e459e882122 | 2017-03-02 01:51:35 |                  |     5110 |         10124 |        4 | 41160 | bde6a7ce-32c2-11e2-92a8-c692850d4a80
    		 00000000073    |         493920 | bd63f5ba-55f1-11e5-5d98-0e459e882122 | 2017-03-02 01:51:35 |                  |     5110 |               |        1 |   111 | e6b5251c-feb3-11e6-ef88-26a4bef88324
    		 
    		 1.		Пользоваетель добавляет инвойс через сайт, соответствие позициций заказа сохраняется через order_data_id, а то что в инвойсе через item_uuid
    		 2.		Если в 1с кто-то удаляет позицию, то мы в базе также обязаны удалить запись и из invoice_data
    		 2.1		Алгоритм поиска записи:
    		 2.1.1			получаем из этого запроса все текущие позиции с item_uuid
    		 2.1.2.			ищем такую запись в массиве $invoice['Товары']. Если не найдено, то строку нужно удалить
    		 2.2		Процесс удаления:
    		 2.2.1			перед удалением нам надо изменить в таблице order_data стаус на  OrderState::CANCELED -> Отменен
    		 2.2.2			затем удалить запись из invoice_data, так как в инвойсе этой строки не существует
    		 3.		Если в 1с кто-то добавляет позицию, а в CMR такой позиции нет, то мы должны будем ее добавить в invoice_data
    		 3.1.		Добавляем запись без указания order_data_id. Это будет говорить о том, что в счете есть такая позиция, а в CRM она никуда не привязана
    		 3.2.		Необходимо в веб сокете передать эту информацию и на сторое JS сделать информирование и обработку этих данных
    		 4.		Необходимо сравнить все позиции и высчитать новые значения
    		 5.		Пустая выборка означает, что полученный счет никуда не привязан
    		*/
    		
    		return $sth;
    	}
    	
    	private function deleteInvoiceDataOnDeleteInERP($invoice, &$data, &$structure)
    	{
    		$db = $this->db;
    		$socket = array();
    		
    		$checkData = $data;
    		
    		// пробегаем по всему товарам в счете и ансетим их в дате.
    		// по окончанию, если в дате что-то остается, то мы это и удалим
    		foreach ($invoice['Товары'] as $good) {
    			foreach ($checkData as $key => $row) {
    				if ($good['Номенклатура'] == $row['item_uuid']) {
    					unset($checkData[$key]);
    				}
    			}
    		}
    
    		
    		if (count($checkData)) {
    			$sth = $db->prepare("UPDATE order_data SET status = ? WHERE order_data_id = ?");
    			$std = $db->prepare("DELETE FROM invoice_data WHERE item_uuid = ?");
    			foreach ($checkData as $key => $row) {
    				$sth->execute(OrderState::CANCELED,$row['order_data_id']);
    				if ($row['order_data_id']) {
    					$std->execute($row['item_uuid']);
    					$structure['order_data'][] = array ($row['order_data_id'] => 'canceled');
    					$socket[] = "Motion:change:".$row['order_data_id'].":".OrderState::CANCELED.":Status";
    				}
    				unset($data[$key]);
    			}
    		}
    		
    		return $socket;
    	}
    	
    	private function insertInvoiceDataOnNewRowInERP($invoice, $data, &$structure)
    	{
    		$db = $this->db;
    		$socket = array();
    		$order_id = null;
    		
    		// пробегаем по всему позициям из даты и удаляем из в инвойсе
    		// подсчет, потому то мы могли все удалить в deleteInvoiceDataOnDeleteInERP
    		if (count($data)) {
    			foreach ($data as $row) {
    				$order_id = $row['order_id'];
    				foreach ($invoice['Товары'] as $key => $good) {
    					if ($row['item_uuid'] == $good['Номенклатура']) {
    						unset($invoice['Товары'][$key]);
    					}
    				}
    			}
    			if (count($invoice['Товары'])) {
    				foreach ($invoice['Товары'] as $row) {
    					$insert = array(
    						'invoice_uuid' => $invoice['Ref'],
    						'item_uuid' => $row['Номенклатура'],
    						'quantity' => $row['Количество'],
    						'price' => $row['Цена'],
    					);
    					$socket[] = Driver1c::SOCKET_MARKER."NewInvoiceItem:".$row['Номенклатура'].":order_id:".$order_id;
    					$db->insert("invoice_data",$insert);
    				}
    			}
    		}
    		return $socket;
    	}
    	
    	private function compareInvoiceBetweenCRMandERP($invoice, $data, &$structure)
    	{
    		$db = $this->db;
    		$socket = array();
    		
    		// подсчет, потому то мы могли все удалить в deleteInvoiceDataOnDeleteInERP
    		if (count($data)) {
    
    			$newArray = $data;
    			$shift = array_shift($newArray);
    			
    			$structure['debug'][] = 'Номер заказа: ' . $shift['order_id'];
    			
    			$difference = array();
    			if ($shift['invoice_number']		!= $invoice['Number']			){ $difference['invoice_number']	= $invoice['Number']; 			}
    			if ($shift['invoice_amount']		!= $invoice['СуммаДокумента']	){ $difference['invoice_amount']	= $invoice['СуммаДокумента'];	}
    			if ($shift['invoice_customer']		!= $invoice['Контрагент']		){ $difference['invoice_customer']	= $invoice['Контрагент'];		}
    			if ($shift['date_check']			!= $shift['invoice_date']		){ $difference['invoice_date']		= $invoice['Date'];				}
    			
    			if (count($difference))
    			{
    				// Ставим в структуру апдейт, что у нас произошли изменения
    				$structure['action'] = "update";
    				$structure['order_id'] = $shift['order_id'];
    
    				// Обновляем данные по инвойсу в базе
    				$db->update("invoices", $difference, "invoice_uuid = ?", $invoice['Ref']);
    			}
    			
    			// теперь проверяем данные по каждой строчке
    			foreach ($data as $row) {
    				$structure['debug'][] = 'сравниваем каждый товар';
    				
    				$item = Utils1c::findItemByUUID($invoice['Товары'], $row['item_uuid']);
    				
    				if ($item == null) {
    					throw new Exception(
    						"Algorithm error, can't find row in ERP data : " . $row['item_uuid']
    					);
    				}
    				
    				$difference = array();
    				if ($item['Количество']	!= $row['quantity']			){ $difference['quantity']		= $item['Количество']; 	}
    				if ($item['Цена']		!= $row['price']			){ $difference['price']			= $item['Цена'];		}
    				
    				//FIXME: а почпему мы изменяем только количество, когда и цену должны обновлять
    				
    				if (count($difference)) {
    					$db->update("invoice_data", $difference, "order_data_id = ?", $row['order_data_id']);
    					
    					if ($difference['quantity']) {
    						// Изменяем данные в самом заказе
    						$db->du("UPDATE order_data SET quantity = ? WHERE order_data_id = ?", $item['Количество'], $row['order_data_id']);
    						// Информируем через сокет, что количество изменилось
    						$socket[] = "Motion:change:{$row['order_data_id']}:{$item['Количество']}:Quantity";
    					}
    					$structure['order_data'][] = array ($row['order_data_id'] => 'update');
    				} else {
    					$structure['order_data'][] = array ($row['order_data_id'] => 'nochange');
    					$structure['debug'][] = "В счете количество={$item['Количество']}, в заказе {$row['quantity']}, изменения не требуются";
    				}
    			}
    		}
    		
    		return $socket;
    	}
    }
    ?>
  • Как найти строку, длинна которой максимально близка нужной?

    Falseclock
    @Falseclock
    SELECT
        city_id,
        city_name, 
        length(trim(city_name)),
        CASE WHEN length(trim(city_name)) = 5 THEN 1 
            WHEN length(trim(city_name)) = 4 THEN 2 
            WHEN length(trim(city_name)) = 6 THEN 3 
        ELSE 1000 END AS ord 
    FROM 
        cities 
    ORDER BY 
        ord ASC 
    LIMIT 20;
    
     city_id | city_name | length | ord
    ---------+-----------+--------+-----
        9754 | Париж     |      5 |   1
       35180 | Актау     |      5 |   1
       35188 | Аягоз     |      5 |   1
       35178 | Аксай     |      5 |   1
       35195 | Есиль     |      5 |   1
       35238 | Темир     |      5 |   1
       35236 | Тараз     |      5 |   1
       35182 | Алга      |      4 |   2
       35249 | Эмба      |      4 |   2
       35194 | Есик      |      4 |   2
       35179 | Аксу      |      4 |   2
       35176 | Абай      |      4 |   2
       35184 | Арыс      |      4 |   2
       35177 | Акколь    |      6 |   3
       35214 | Ленгер    |      6 |   3
       35257 | Астана    |      6 |   3
       35222 | Риддер    |      6 |   3
       35250 | Шалкар    |      6 |   3
       35187 | Атырау    |      6 |   3
       35209 | Кентау    |      6 |   3
    (20 строк)
  • Как найти строку, длинна которой максимально близка нужной?

    Falseclock
    @Falseclock
    city_id | city_name | length | ord
    ---------+-----------+--------+-----
       35238 | Темир     |      5 |   1
       35176 | Абай      |      5 |   1
       35184 | Арыс      |      5 |   1
       35180 | Актау     |      5 |   1
        9754 | Париж     |      5 |   1
       35178 | Аксай     |      5 |   1
       35195 | Есиль     |      5 |   1
       35188 | Аягоз     |      5 |   1
       35249 | Эмба      |      4 |   2
       35194 | Есик      |      4 |   2
       35182 | Алга      |      4 |   2
       35251 | Шар       |      4 |   2
       35179 | Аксу      |      4 |   2
       35243 | Ушарал    |      6 |   3
       35236 | Тараз     |      6 |   3
       35244 | Уштобе    |      6 |   3
       35234 | Талгар    |      6 |   3
       35257 | Астана    |      6 |   3
       35177 | Акколь    |      6 |   3
       35187 | Атырау    |      6 |   3
    (20 строк)