Skrypty shell oraz wysyłanie maili w CakePHP
Na praktycznym przykładzie pokażę jak tworzyć w CakePHP skrypty shella oraz pokażę obsługę wysyłania maili. Naszym zadaniem jest stworzenie systemu powiadomień mailowych do zadań (wydarzeń), które są przechowywane w bazie danych w tabeli tasks. Kod sql tworzący tę tabelę poniżej.
CREATE TABLE `tasks` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) COLLATE utf8_polish_ci NOT NULL, `description` varchar(255) COLLATE utf8_polish_ci DEFAULT NULL, `date` date NOT NULL, `status` tinyint(4) DEFAULT NULL, `remind` text COLLATE utf8_polish_ci, `remindemail` varchar(255) COLLATE utf8_polish_ci DEFAULT NULL, `created` datetime NOT NULL, PRIMARY KEY (`id`) )oraz dodający do tabeli kilka wierszy:
INSERT INTO `tasks` (`id`, `title`, `description`, `date`, `status` `remind`, `remindemail`, `user_id`, `created`) VALUES (2, 'qewtrwet', '', '2014-10-20', 0, NULL, '', 1, '2014-10-06 14:31:37'), (3, 'qrwterytew', 'sgdsfg ghdfhf', '2014-10-24', 0, NULL, '', 1, '2014-10-07 09:21:22'), (5, 'Faktura dla firmy ABC', 'Wystawić fakturę', '2014-10-31', 0, '["1"]', '[email protected]', 1, '2014-10-07 14:31:51'), (6, 'Spotkanie', 'Spotkanie organizacyjne', '2014-10-31', 0, '["1","2"]', '[email protected]', 1, '2014-10-07 14:39:48'), (7, 'Raport', 'Wygenerować raport', '2014-10-31', 0, '["1","2"]', '[email protected]', 1, '2014-10-07 15:23:47'), (8, 'Wydrukować dokumenty', 'Wydrukować dokumenty dla firmy ABC', '2014-10-31', 0, NULL, '', 1, '2014-10-07 15:24:54');Oczywiście musisz zmodyfikować pole date na daty przyszłe oraz pole remindemail na swój adres email. Informacja o tym czy wysyłać przypomnienie dla zadania jest zapisana w polu remind (jako tekst json). NULL oznacza brak przypomnienia. Na przykład jeśli wartość pola to ["1"] oznacza to, że musimy wysłać powiadomienie dzień przed zadaniem, jeśli ["1","2","3"] - dzień, tydzień oraz dwa tygodnie przed zadaniem. Pole status zawiera informację czy zadanie zostało zrealizowane (wartość 1) czy nie (wartość 0). Powiadomienia będziemy wysyłać tylko do zadań niezrealizowanych. W polu remindemail jest zapisany email, na który ma być wysłane powiadomienie. Należy utworzyć (wypiec) odpowiedni model (Task), kontroler (TasksController) i widok dla tej tabeli.
Skrypty shell w CakePHP
Skrypt shella jest idealnym rozwiązaniem naszego zadania. Taki skrypt wywołuje się z konsoli. Można go wykonać również jako zadanie crona (w naszym przypadku tak trzeba będzie zrobić, aby wywoływać skrypt codziennie w celu sprawdzenia czy są zbliżające się wydarzenia i ewentualnie wysłać maila z powiadomieniem). Skrypty shell umieszcza się w katalogu app/Console/Command. Stwórzmy w tym katalogu plik ReminderShell.php o następującej zawartości.
class ReminderShell extends AppShell { public function main() { $this->out('Witaj'); } }Jest to bardzo prosty skrypt, który wypisuje na ekranie tekst "Witaj". Wywołujemy go z konsoli. Na Windowsie najpierw przechodzimy w konsoli (cmd) do katalogu z php i następnie wywołujemy skrypt
php.exe /sciezka/do/app/Console/cake.php reminderNa Linuxie wpisanie w terminalu /sciezka/do/app/Console/cake reminder powinno wystarczyć. Była to tylko prosta demonstracja działanie skryptów shella. Teraz napiszemy właściwy "przypominacz". Usuń z klasy ReminderShell metodę main(). Dodaj natomiast co następuje.
class ReminderShell extends AppShell { public $uses = array('Task'); public function remind() { } }Pusta metoda remind (tutaj będzie właściwa część skryptu). Zapis public $uses = array('Task') oznacza, że chcemy w naszym shell skrypcie korzystać z modelu Task (robić zapytania). Jeśli chcielibyśmy użyć innych modeli trzeba by je było podać po przecinku. Pełny kod skryptu poniżej.
class ReminderShell extends AppShell { public $uses = array('Task'); public function remind() { App::uses('CakeEmail', 'Network/Email'); $Email = new CakeEmail('smtp'); $all_tasks = $this->Task->find('all', array('conditions' => array('date >= NOW()', 'status' => 0))); foreach($all_tasks as $task) { if(!empty($task['Task']['remind'])) { if(!empty($task['Task']['remindemail'])) { $remind_email = $task['Task']['remindemail']; } else { $remind_email = null; } $task['Task']['remind'] = json_decode($task['Task']['remind']); foreach($task['Task']['remind'] as $remind_option) { $current_date = mktime(0,0,0,date('n'),date('j'),date('Y'),0); if($remind_option==1) {//Dzień przed terminem if($current_date+DAY==strtotime($task['Task']['date'])) { if(!empty($remind_secondary_email)) { $Email->to($remind_email); } $Email->subject('Przypomnienie o zadaniu: '.$task['Task']['title']); $message = 'Przypomnienie o zadaniu na jutro ('.$task['Task']['date'].'): '.$task['Task']['title'].' '.$task['Task']['description']; $Email->send($message); } } if($remind_option==2) {//Tydzień przed terminem if($current_date+7*DAY==strtotime($task['Task']['date'])) { if(!empty($remind_secondary_email)) { $Email->to($remind_email); } $Email->subject('Przypomnienie o zadaniu: '.$task['Task']['title']); $message = 'Przypomnienie o zadaniu za tydzień ('.$task['Task']['date'].'): '.$task['Task']['title'].' '.$task['Task']['description']; $Email->send($message); } } if($remind_option==3) {//Dwa tygodnie przed terminem if($current_date+14*DAY==strtotime($task['Task']['date'])) { if(!empty($remind_secondary_email)) { $Email->to($remind_email); } $Email->subject('Przypomnienie o zadaniu: '.$task['Task']['title']); $message = 'Przypomnienie o zadaniu za 2 tygodnie ('.$task['Task']['date'].'): '.$task['Task']['title'].' '.$task['Task']['description']; $Email->send($message); } } } } } } }Omówię teraz jego działanie. Wszystko dzieje się w metodzie remind. Obsługa maili Dzięki linijce
App::uses('CakeEmail', 'Network/Email');informujemy, że chcemy korzystać z klasy CakeEmail Caka. Następnie wywołujemy
$Email = new CakeEmail('smtp');Przekazany parametr 'smtp' oznacza, że chcemy wykorzystać konfigurację smtp z pliku konfiguracyjnego maili czyli app/Config/email.php. Jeśli jeszcze nie masz takiego pliku skopiuj plik email.php.default, zmień mu nazwę na email.php i zmień konfigurację na własną. Przykładowa konfiguracja smtp:
public $smtp = array( 'transport' => 'Smtp', 'from' => array('[email protected]' => 'Terminarz'), 'host' => 'costam.pl', 'port' => 587, 'username' => '[email protected]', 'password' => 'haslo' );Kolejna linijka w skrypcie shella to
$all_tasks = $this->Task->find('all', array('conditions' => array('date >= NOW()', 'status' => 0)));Pobiera on wszystkie zadania z datą dzisiejszą i późniejszą oraz ze statusem 0 czyli niezrealizowane. Następnie przechodzimy przez tablicę wyników i sprawdzamy czy kolumna remind jest niepusta. Jeśli jest niepusta to do zmiennej $remind_email zapisujemy email na jaki mamy wysłać powiadomienie. Dekodujemy opcje powiadomień z jsona na tablicę phpową i przechodzimy przez nią sprawdzając wszystkie możliwe opcje (1, 2 i 3). Omówię tylko fragment dotyczący opcji 1 - powiadomienie dzień przed, reszta jest analogiczna.
if($current_date+DAY==strtotime($task['Task']['date'])) { if(!empty($remind_secondary_email)) { $Email->to($remind_email); } $Email->subject('Przypomnienie o zadaniu: '.$task['Task']['title']); $message = 'Przypomnienie o zadaniu na jutro ('.$task['Task']['date'].'): '.$task['Task']['title'].' '.$task['Task']['description']; $Email->send($message); }Porównujemy datę zadania zapisaną w bazie z dzisiejszą datą + 1 dzień i jeśli jest zgodna wysyłamy email z powiadomieniem. Adres email na jaki ma być wysłana wiadomość ustawiamy za pomocą
$Email->to($remind_email);Temat określa się metodą subject. Natomiast za samo wysłanie odpowiada metoda send. Jako parametr przekazujemy jej treść maila. Skrypt shella wywołujemy pisząc w konsoli: na Windowsie najpierw przechodzimy w konsoli do katalogu z php i następnie wywołujemy skrypt
php.exe /sciezka/do/app/Console/cake.php reminder remind
Na Linuxie: /sciezka/do/app/Console/cake reminder remind.Skrypt taki aby spełniał nasze założenia powinien być uruchamiany codziennie za pomocą crona.