Відсутність add message php. Виведення повідомлень користувачу у веб-застосунках. PHP скрипт для операцій CRUD

Зрештою я зрозумів: краще за дружину нікого не знайти. Залишилось тільки знайти дружину

PHP AJAX CRUD: створення, видалення, редагування записів у БД MySQL

У цій статті ми дізнаємось про те, як додавати, редагувати та видаляти записи в базі даних MySQL, використовуючи PHP. Ми використовували обробник JQuery, який надсилає AJAX запит до серверного скрипту. Обробник оновлює список записів.

AJAX форма для надсилання запитів на створення, видалення, редагування

При додаванні запису форма надсилає дані PHP скрипту через запит AJAX. У разі успішного додавання, список записів перезавантажується.

JQuery AJAX функції для запиту до бази даних CRUD

У функції JQuery AJAX ми маємо перемикач випадки додати редагувати і видаляти. Ці випадки генерує різні рядки запиту та відповіді-дані залежно від дій бази даних.

PHP скрипт для операцій CRUD

Наступний код виконує запити до бази даних. Цей скрипт PHP після виконання CRUD дії оновлює записи в результаті відповіді AJAX.

require_once("dbcontroller.php"); $db_handle = новий DBController(); $action = $_POST["action"]; if(!empty($action)) ( switch($action) ( case "add": $result = mysql_query("INSERT INTO comment(message) VALUES("".$_POST["txtmessage"]."")" );if($result)($insert_id = mysql_insert_id(); echo "

" . $_POST["txtmessage"] . "
"; ) break; case "edit": $result = mysql_query("UPDATE comment set message = "".$_POST["txtmessage"]."" WHERE id=".$_POST["message_id"]); if( $result) echo $_POST["txtmessage"]; break; "]); ) break; ))

у моєму Zend-додатку я пишу трохи API для мобільних додатків. Щоб спростити роботу з мобільними розробниками, я використовую Swagger. До цих пір все працює чудово, за винятком одного GET-запиту.

Коли я викликаю /user/messages/(sessionToken)? NumMessages = (numMessages) & pageNr = (pageNr) у браузері, я отримую результати, які хочу, але коли я намагаюся дозволити Swagger виконати цей запит, передається лише sessionToken. Я спробував ці інструкції для Swagger:

/** * @SWG\Api(path="/user/messages/(sessionToken)?numMessages=(numMessages)&pageNr=(pageNr)", * @SWG\Operation(* method="GET", * summary=" Gets messages paged", * notes="", * type="string", * nickname="getUsermessagesPaged", * authorizations=(), * @SWG\Parameter(* name="sessionToken", * description="The token from an active user session", * required=true, * type="string", * paramType="path", * allowMultiple=true *), * @SWG\Parameter(* name="numMessages", * description=" номер messages на сторінці (numMessages & pageNr ignored if not both are set)", * required=true, * type="string", * paramType="query", * allowMultiple=true *), * @SWG\Parameter (* name="pageNr", * description="pagenumber (numMessages & pageNr є не звільнені, якщо не має значення)", * required=true, * type="string", * paramType="query", * allowMultiple=true *), * @SWG\ResponseMessage(code=200, message="json (messages => "user_messages")"), * @SWG\ResponseMessage(code=400, message="json with err or "not logged in"") *) *) */

Хтось бачить мою помилку?

Будь-яка допомога вітається.

З повагою

Відновлення. Як було запропоновано, я змінив обидва paramTypes на "query" і змінив шлях:

@SWG\Api(path="/user/messages/(sessionToken)",

але він не працював винищувачем.

xdebug в eclipse PDT показує:

RequestURI => /ias/public/user/messages/(sessionToken)

QueryParams => Zend\\Stdlib\\Parameters - *ArrayObject*storage => Array - =>

чванство JSON:

( "apiVersion": "1.0.0", "swaggerVersion": "1.2", "apis": [ ( "path": "\/user", "description": "Operations about users" ) ], "info" : ( "title": "Mobile access api", "description": "This is the xxx mobile access api.", "termsOfServiceUrl": null, "contact": "xxx", "license": null, "licenseUrl" : null, "_partialId": null, "_partials": , "_context": ( "comment": "\/**\ * @SWG\\Info(\ * title="(!LANG:Mobile access api",\ * description="This is the xxx mobile access api.",\ * contact="xxx",\ *)\ *\/", "line": 3 } } } !}

Ось вихід /user:

( "basePath": "http://localhost/ias/public", "swaggerVersion": "1.2", "apiVersion": "1.0.0", "resourcePath": "/user", "apis": [ ( "path": "/user/balance/(sessionToken)", "operations": [ ( "method": "GET", "summary": "Gets userbalance", "nickname": "getUserdata", "type": "string", "parameters": [ ( "paramType": "path", "name": "sessionToken", "type": "string", "required": true, "allowMultiple": false, "description": "The token from an active user session" ) ], "responseMessages": [ ( "code": 200, "message": "json (balance => "user_balance")" ), ( "code": 400, "message ": "json with error "not logged in"" ) ], "notes": "", "authorizations": () ) ] ), ( "path": "/user/login", "operations": [ ( "method": "POST", "summary": "Logs user in the system", "nickname": "loginUser", "type": "string", "parameters": [ ( "paramType": "form", "name": "email", "type": "string", "required": true, "allowMultiple": false, "description": "The user email for login" ), ( "paramType": "form", "name": "password", "type": "string", "required": true, "allowMultiple": false, "description": "The password for login in clear text" ) ], "responseMessages": [ ( "code": 200, "message": "json with session_id, user_id, user_balance" ), ( "code": 400, "message": "json with error "no user with given email and password" " ), ( "code": 400, "message": "json with error "invalid input"" ), ( "code": 400, "message": "json with error " no post request"" ) ], "notes": "", "authorizations": () ) ] ), ( "path": "/user/logout", "operations": [ ( "method": "POST" , "summary": "Logs user out", "nickname": "logoutUser", "type": "string", "parameters": [ ( "paramType": "form", "name": "sessionToken", " type": "string", "required": true, "allowMultiple": false, "description": "The token from an active user session" ) ], "responseMessages": [ ( "code": 200, "message" : "json (result => "deleted")" ), ( "code": 400 , "message": "json with error "user_session with given sid"" ), ( "code": 400, "message": "json with error "invalid input"" ), ( "code": 400, "message ": "json with error "no post request"" ) ], "notes": "", "authorizations": () ) ] ), ( "path": "/user/messages/(sessionToken)", "operations ": [ ( "method": "GET", "summary": "Gets new messages", "nickname": "getUsermessages", "type": "string", "parameters": [ ( "paramType": "path ", "name": "sessionToken", "type": "string", "required": true, "allowMultiple": false, "description": "The token from an active user session" ) ], "responseMessages": [ ( "code": 200, "message": "json (messages => "user_messages")" ), ( "code": 400, "message": "json with error "not logged in"" ) ], " notes": "", "authorizations": () ), ( "method": "GET", "summary": "Gets messages paged", "nickname": "getUsermessagesPaged", "type": "string", " parameters": [ ( "paramType": "path", "name": "sessionToken", "ty pe": "string", "required": true, "description": "The token from an active user session" ), ( "paramType": "query", "name": "numMessages", "type": " string", "required": true, "description": "число messages на сторінці (numMessages & pageNr є не вказано, якщо не вистачає)" ), ( "paramType": "query", "name": "pageNr" , "type": "string", "required": true, "description": "pagenumber (numMessages & pageNr are ignored if not both are set)" ) ], "responseMessages": [ message": "json (messages => "user_messages")" ), ( "code": 400, "message": "json with error "not logged in"" ) ], "notes": "", "authorizations" : () ) ] ), ( "path": "/user/userdata", "operations": [ ( "method": "POST", "summary": "Posts userdata", "nickname": "postUserdata", "type": "string", "parameters": [ ( "paramType": "form", "name": "sessionToken", "type": "string", "required": true, "allowMultiple": false, "description": "The token from an active user session " ), ( "paramType": "form", "name": "password", "type": "string", "required": false, "allowMultiple": false, "description": "new password" ), ( "paramType": "form", "name": "address", "type": "string", "required": false, "allowMultiple": false, "description": "new address" ), ( "paramType ": "form", "name": "housenr", "type": "string", "required": false, "allowMultiple": false, "description": "new housenr" ), ( "paramType": " form", "name": "zip", "type": "string", "required": false, "allowMultiple": false, "description": "new zip" ), ( "paramType": "form", "name": "city", "type": "string", "required": false, "allowMultiple": false, "description": "new city" ), ( "paramType": "form", "name" : "email", "type": "string", "required": false, "allowMultiple": false, "description": "new email" ) ], "responseMessages": [ ( "code": 200, "message ": "json (user => "userdata")" ), ( "code": 400, "message": "json with error

Здається, помилка, що мій swagger-ui не надсилає жодних параметрів запиту? Ось приклад з одним параметром query-param, sessionToken: (контролюється FireBug 2.0.6)

GET /ias/public/user/balance HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0 Accept: application/json Accept-Language: en-US;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: application/json Referer: http://localhost/ias/swagger/ Cookie: __utma=111872281.581414660.136670753222000 uvts=sB5Dda3cZBNdaTk; searchpanel-close=set Connection: keep-alive

Відповідь була:

HTTP/1.1 400 Bad Request Date: Tue, 25 Nov 2014 14:58:20 GMT Server: Apache/2.4.9 (Win32) PHP/5.5.12 X-Powered-By: PHP/5.5.12 Content-Length: 25 Connection: close Content-Type: application/json; charset=utf-8

Відповідь була вірною, тому що не було передано sessionToken.

Це вимагає роботи, але це не походить від swagger-ui:

GET /ias/public/user/balance?sessionToken=HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0 Accept: text/html,application/ xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: de,en-US;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: __utma=111872281.581414660 .1366700677.1394721873.1394723866.255; uvts=sB5Dda3cZBNdaTk; searchpanel-close=set Connection: keep-alive

Overview

Будівля в notification system, перша в Joomla, дозволяє вашому застосуванню до користування користувачем (або групою користувачів) повідомлено про різні дії. Примітка, що notifications as important alerts that user would be interested to read and keep track of it.
Notifications can be generated everywhere. У вашому компоненті або plugins і останніх зображені всередині JomSocial notification system.
Цей літературний текст буде показаний, але не вважається, що будь-яка тридцятирічна частина буде використовувати:)
Якщо ви не знаєте, як створювати інструменти, які будуть triggered on this event, we suggest you to check this guide

Implementing it in your component anyway

Як встановлений в перегляді цього літератури, ми будемо створювати notifications using community plugin.
Ви будете мати можливість створювати повідомлення всередині свого компонента, або ваш plugin. The following tutorial will work в any of this cases. Ви повинні потрібні для того, щоб визначити, наскільки точка в вашому коді notification буде створено і just load the JomSocial Core Libraries file.

require_once JPATH_ROOT . "/components/com_community/libraries/core.php";

Зробивши tutorial explained bellow will work just fine for your extension too

Preparing the Development Environment

1. We will assume that you"re already created community type example plugin which will be triggered , коли user changes its profile
Якщо ви не можете скакнути автентичне посилання plugin від , ввімкнути його в Joomla and enable plugin. It is named Community - Notifications Example
2. Navigate to your database and empty these two tables, so they dont have any records at all

A) prefix_community_notification
b) prefix_community_mailq

3. Have at least two (2) users at your test sites and know their ID"s

У попередніх версіях Joomla, user ID's буде все started from specified number (62, 42) In Joomla 3, this number will be random, hence, the picture of our testing environment because it will definitely at your

The First Notification

Open the plugin php file which will be located в ROOT/plugins/community/example
Within the функція onAfterProfileUpdate() replace the

CNotificationLibrary:: add ($cmd, $actor, $target, $subject, $body, $template, $params);

Як показано на прикладі, notification add api have 7 parameters

  1. $cmd- is the notification type. Ви можете додати всі типи повідомлень у цьому файлі. ROOT/components/com_community/libraries/notificationtypes.php starting from, or around line 53. We recommend using system_messaging notification type.
  2. $actor- is the person who carry out the action
  3. $target- is the person, or group of people that will receive notification
  4. $subject- is the notification subject, in both, notification popup window and the email title
  5. $body- is the body of email notification message
  6. $template- якщо вам потрібний конкретний template to use, ви можете define it here. Іншийwise, this parameter can be empty
  7. $params- custom defined parameters

Knowing all this, lets define the variables we will use
Change your plugin code to:

$user = CFactory:: getUser(); $cmd = "system_messaging"; // перший парам, тип діяльності$actor = $user -> id; //second param - get the id of $actor$ target = "965"; // Third param. Who receive notification? У нашому environment, його admin user with id 965. При вашому environment, ви будете дуже добре, щоб отримати ID від вашого об'єкта або від array of users.$subject = "Notification Subject" ; // Subject of both, email and popup notifications$body =; // Body message in emails. $template = ""; // Якщо ви потрібні використовувати specific jomsocial template file, ви можете define it here.$params = new CParameter("") ; // We want to create an additional params object, and assign data to it, без будь-якого formally define a class CNotificationLibrary:: add ($cmd, $actor, $target, $subject, $body, $template, $params);

Новий реєстрація з будь-яким користувачем і зміна profile information. Lets go to database to see what happened.
Navigate to prefix_community_notifications table and observe the new record

Navigate to prefix_community_mailq table, and see the new record

Congratulations!- Ви успішно створили вашу першу власну власні notification, яку ви були dispatched via email and internal JomSocial notification system


Potential Code Bloat

Above example is fine, it it works, але це не є recommended to use it like that. Instead, it could be written like this

$actor = CFactory:: getUser(); $params = new CParameter("") ; CNotificationLibrary:: add ("system_messaging" , $actor -> "This is the notification body message", "", $params);

Це є дуже чистий і спричинений тим, що basically doing absolutely sama thing as a code shown above.

Custom Notification Parameters

A notification API може бути розширений з будь-яким param і ви повинні бути як add.
Ці параметри можуть бути отримані до будь-якого електронної пошти, notification, і course, language file.

$actor = CFactory:: getUser(); $link = "http://www.google.com"; $params = new CParameter("") ; $params -> set ("actor", $actor -> getDisplayName()); // can be used as (actor) tag $params -> set ("actor_url" , "index.php?option=com_community&view=profile&userid=". $ actor -> id); // Link for the (actor) tag $params -> set ("url", $link); //url of the whole activity. Використовується, коли ходьба над avatarом в notification window. Can be used as (url) tag in outgoing emails too. Make sure that you have defined $link variable:) CNotificationLibrary:: add ( "system_messaging" , $actor -> id , "965" , "Notification Subject" , "This is the notification body message", "", $params);

  • $params = новий CParameter( ); - Ви будете створювати новий параметр об'єкта, і оцінити його, без будь-якого розміру define a class.
  • $params->set("actor", $actor->getDisplayName());- Your notification should always have an actor. Цей param може бути прописаний до template as (actor) tag. У notification window, it defines the user that carries an action.
  • $params->set("actor_url", "index.php?option=com_community&view=profile&userid=" . $actor->id);- Actor URL is usually the url of an actor. In notification popup, it adds the link to the (actor) element
  • $params->set("url", $link);- Це є найбільш важливим paramater you must always set properly. У notification window, ця парам використовується за допомогою зображення. У електронної пошти, вона займається місцем, де діяльність надсилається.

Для цього example, we will set variable $link to lands on www.google.com so you can see how it works

Adding Language String and Use Params

За допомогою параметрів, які належать вам, можна використовувати в наших мовних файлах як добре.
Lets define the language keys by altering the " CNotificationLibrary::add() API

CNotificationLibrary:: add ( "system_messaging" , $actor -> id , "965" , JText:: sprintf ( "PLG_COMMUNITY_EXAMPLE_SUBJECT") , JText:: sprintf ("PLG_COMMUNITY_EXAMPLE_BODY" ) , "" , $params ) ;

The language file should look like this

PLG_COMMUNITY_EXAMPLE_SUBJECT = "(actor) updated profile" PLG_COMMUNITY_EXAMPLE_BODY = "Hi Admin \n Це є електронною поштою, щоб дізнатися, що (actor) updated profile \n\n Якщо ви хочете, щоб Google, click here \n a href=" _QQ_"(url)" _QQ_">(url)"

У цьому прикладі, ми використовуємо tag (actor) and (url) to pass the data to both, notification, and email templates. Lets see how does that look.
In notification window when hovering over avatar, notice the (url) param kicked in and adds link to google over avatar. It is intentional , because we made it that way:)


]

У тій самій window, коли ходьба над actor link. This is the part where (actor) echoed the user that carries an action, while (actor_url)" taken care that object is linked properly


Lets see what happens in the email queue


І, нарешті, поточний електронний посібник, що є sent end user


Success
Till now, we created three (3) parameters that are successfully used in notification window, and emails.

  1. (actor)- Returns the username of the user which carries the action
  2. (actor_url)- Gives the attribute to the (actor)
  3. (url)- Це не mandatory, але ви повинні будь-який у вашому notification. It is the main url where action that we are notified happened.

Similarly, you can define "

  • (target)- if you need it
  • (target_url) if you need it in your notification.
  • (title)- Commonly used to refer to an object that generated notification. Example: "User X posted new photo in Album Y." Album Y is title here
  • (title_url)- Як з попередньою оновленням, url of an object that generated notification.
  • (message)- Цей парадокс може бути використаний для набору (і echo) message in the body of JomSocial email.
3.3K

Виведення повідомлень користувачеві - досить поширена дія, яку має виконувати веб-додаток. Воно може відбуватися при обробці форм, це можуть бути повідомлення про помилки, а також повідомлення, які говорять, що треба зареєструватися, коли користувач намагається отримати доступ до обмеженої частини сайту, та в багатьох інших випадках.

Дуже часто створення та виведення повідомлень рознесено за різними HTTP-запитами. Як правило, зручно буває використовувати редирект після обробки форм (щоб уникнути проблем з кнопками Back і Refresh), але в той же час природний момент для створення повідомлення - це саме момент обробки форм та здійснення дій, що його супроводжують. Чому? Уявіть, що текст повідомлення повинен виглядати приблизно так: "Кількість одиниць товару 'Килимок для миші', що замовляються, успішно змінено з 7 до 12". Після редиректу, можливо, на зовсім іншу з точки зору функціональності сторінку, це буде зайва головна - визначити, що було скоєно до цього.

Найчастіше повідомлення виводять саме в POST-запиті, який займається обробкою форми - це погано, написи "ця сторінка застаріла" псують життя (коли користувачеві заманеться спробувати кнопку Back).

Хтось використовує редирект, махнувши рукою на дружні повідомлення.

У той же час є простий і очевидний спосіб зробити життя кращим. Незважаючи на очевидність, мені чомусь не доводилося бачити, щоб хтось його використав – принаймні коли я дивився чужі вихідники.

Отже, маємо проблему – повідомлення має "жити" у різних запитах. Нам потрібен механізм передачі тексту повідомлення на сторінку, яка має виводити. Ви вже, мабуть, згадали про сесію.

Так, взагалі-то ви маєте рацію. Інші способи, наприклад, через глобальну змінну, не дозволяють зберегти дані у разі, коли використовується редирект (зауваження Максима Науменка). Плюс ще я зазвичай роблю так, щоб кожен екран у додатку мав можливість, поряд з іншою інформацією, виводити повідомлення, які були сформовані на попередніх екранах. Це зручно, тому що не потрібно готувати окремі екрани для виведення повідомлень, а користувачеві не доведеться ще раз клацати мишею. Але, правда, тут треба подумати дизайнеру - виділити область, в якій з'являлися б повідомлення.

Ідея дуже проста, і її можна реалізувати за допомогою кількох класів.

Перше, що спадає на думку - створити клас Message, який би, власне, і був повідомленням на нашій нехитрій схемі класів. Повідомлення має вміти зберігати себе у сесії, а також виводити себе на екран.

class Message ( /** * Зміст повідомлення. */ var $content; /** * Конструктор для ініціалізації тексту повідомлення. * * @param content зміст повідомлення */ function Message($content) ( $this->content = $content ; ) /** * Запис повідомлення на сесію. " - " . $this->content .
"; } }

Для доступу до сесії використовується змінна $_SESSION.

Зауважу, що $_SESSION - це масив, ми використовуємо лише один елемент цього масиву з індексом 'session_message'.

В даному випадку маємо справу з "масивом масивів" - те, що ми зберігаємо в елементі 'session_message', є масивом, це і є список повідомлень, що передаються (їх, звичайно, може бути кілька).

Якщо ви не змогли намацати нитку, саме час освіжити у пам'яті розділи мануалу, присвячені сесіям та масивам.

У вас може виникнути запитання. А навіщо тут потрібні класи? Цілком можна було обійтися двома функціями. Але давайте зазирнемо далі. Нам може знадобитися створювати повідомлення з різними типами (info, error, warning), визначати адресатів повідомлень.

Зауважте, що на даний момент у сесію кладеться не сам об'єкт, а лише текст повідомлення. ООП дозволяє нам надалі змінити поведінку методу send(), не змінюючи клієнський код, який звертається до цього методу (наприклад, у майбутньому в сесію можна повністю записувати об'єкт Message, якщо в ньому буде багато полів).

Уявімо, що ми це робили б за допомогою функцій. Напевно, ми мали б функцію message_send($txt), ще мав би функцію message_to_page($txt). Тепер треба додати можливість різної поведінки для різних видів повідомлень. Виклики функцій змінюються: message_send($txt, $kind), message_to_page($txt, $kind). Прийде прочитати весь код програми в пошуках таких функцій, роблячи виправлення.

Цього можна уникнути, заздалегідь передбачаючи ситуацію, представивши повідомлення як асоціативного масиву: $msg[‘txt’], $msg[‘kind’], тоді викликах функцій буде лише один параметр. Відчуваєте, як це прагне перетворитися на клас?

Так ось, ООП дає можливість дозволити собі розкіш не продумувати заздалегідь.

Наступний клас - Inbox - саме для цього призначений.

class Inbox ( /** * Масив повідомлень, що надійшли. */ var $messages = array(); /** * У конструкторі отримуємо всі повідомлення, що надійшли * і видаляємо їх з сесії. */ function Inbox() ( if (is_array($ _SESSION["session_messages"])) ( $messages = $_SESSION["session_messages"]; $co = sizeof($messages); for ($i = 0; $i< $co; $i++) { $this->messages = новий Message($messages[$i]); ) ) /* очищаємо масив повідомлень */ $_SESSION["session_messages"] = array(); ) /** * Виводимо на сторінку вміст Inbox. */ function toPage() ( $co = sizeof($this->messages); if ($co > 0) ( echo "Повідомлення від системи:
"; ) for ($i = 0; $i< $co; $i++) { $this->messages[$i]->ToPage(); ) ) )

Давайте перевіримо нашу систему повідомлень.

Створимо дуже простий приклад, який у відповідь на відправлення форми повідомлятиме кількість секунд у поточній хвилині.

send(); /* перенаправлення він * * header("location:"); ) else ( $inbox = new Inbox(); $inbox->toPage(); ) ?>

Всю роботу з масивами та сесіями ми сховали всередині класів, і кінцевий код виглядає просто та красиво.

Створіть каталог на веб-сервері, потім створіть у ньому ці три файли і спробуйте у роботі. Зауважте, проблем із кнопками Back і Refresh не виникає.

А тепер уявіть, що ви створюєте складний портал, де, як правило, на сторінках є кілька блоків, і кожен може містити в собі окрему програму.

Тут ми зустрічаємо дві труднощі:

* Хотілося б, щоб список повідомлень з'являвся у певній частині сторінки, і ви вже підібрали хороше місце для цього.
Проблема в тому, що треба запустити команду $inbox->toPage() саме в той момент, який відповідав би положенню списку повідомлень на сторінці. Якщо ми захочемо змінити положення цього списку, доведеться лізти в код, але погано завжди для цього змінювати каркас порталу. Найкращим рішенням було б зробити висновок повідомлень у вигляді окремого модуля, про який відомо лише, що його треба підключити до каркаса.
Тобто звільнитися від суворої послідовності запуску модулів. Справді, якщо результат роботи виведення Inbox не залежить від роботи системи (на даному кроці – всі дані у нас вже є у сесії), то навіщо зайві складності?
* Щоб підтримувати зовнішній вигляд (дизайн) списку повідомлень, треба дбати про HTML-код, який у нас зашитий у методах toPage() класів Message та Inbox. Як правило, доведеться змінювати PHP-код для того, щоб змінити дизайн.

Щоб спробувати вирішити першу проблему, можна створити буфер, в якому зберігався результат роботи виводу Inbox.

Можливо, у нас ще буде кілька схожих (на Inbox) речей і треба створити систему буферів. Для того, щоб не переплутати де чий висновок, ми, напевно, прийдемо до іменування буферів. У нас десь зберігатиметься послідовність, відповідно до якої має відбуватися виведення буферів - бажано у зовнішньому файлі, щоб легше було вносити зміни.

Вже ця спроба вирішення дає нам ідею використовувати XML як зберігання проміжних даних. А використання стилів XSLT допоможе впоратися й із другою проблемою.

Я не зупинятимусь на тому, що таке XML, і що таке XSLT. Якщо ви не знайомі з цими речами, zvon.org стане гарною точкою для вивчення.

Ідея в тому, щоб у методах toPage() формувати не HTML-код, а структуру XML. Документ сторінки буде створюватися у вигляді стрінга з XML-кодом (він буде служити як "буфер"), а на останній стадії роботи скрипту ми будемо використовувати XSL-трансформацію.

Для початку уявімо, що має бути результатом роботи основної частини коду.

minute 57 second: 45

Що це таке – здогадатися досить просто – два повідомлення та форма. Зауважте, PHP-скрипт повинен підготувати лише такий стрінг – він дуже простий. Причому порядок дотримання основних тегів неважливий. можна поставити спочатку, наприклад, як буде зручно програмісту. Як це реалізувати? Можна, майже нічого не змінюючи, використовувати output buffering, замість HTML коду виводити XML, а в кінці просто захопити виведення в стрінг. Але тоді ми втратимо у гнучкості – наприклад, хочеться іноді виводити налагоджувальну інформацію прямо на сторінку (за допомогою echo). У той же час, розробники PHP працюють над DOM-модулем, який пропонує більш сучасний спосіб створення та передачі деревоподібних документів. Якщо ми захочемо впровадити DOM, то доведеться перекроювати всі програми, змінюючи виведення стрінгів на створення DOM-елементів. Тому я віддаю перевагу зберігати XML-подання об'єктів усередині самих об'єктів, послідовно збираючи загальний XML-документ. Це не так складно, потрібна лише невелика модифікація. Ви побачите, що такий прийом не прив'язаний жорстко до конкретного способу зберігання XML-даних, і це дозволить перейти до використання DOM "малою кров'ю". Насамперед зауважимо, що з кожного нашого об'єкта є метод toPage(). Ця схожість має нас змусити замислитися над тим, щоб запровадити новий спільний батьківський клас. Нехай кожен клас, який здатний створювати шматочки XML-документа для сторінки, успадковуватиметься від класу, який дбатиме про XML-подання об'єкта. Назвемо його Outputable.

class Outputable ( /** * XML контейнер (стрінг). */ var $output = ""; /** * Віддати вміст контейнера та очистити контейнер. * * @return стринг з XML-даними */ function getOutput() ( $ out = $this->output; $this->output = ""; return $out; ) /** * Додати порцію до вмісту контейнера. ->output .= $string . "n"; ) /** * "Абстрактний" метод. */ function toPage() ( ) )

Метод toPage() зроблений порожнім - у разі він потрібен як індикатор того, як повинні зовнішні "матрешки"-класи спілкуватися з внутрішнім класом. Втім, тут можна було б запропонувати реалізацію за умовчанням, якби ми помітили, що є багато об'єктів, які однаково виводять себе на сторінку.

Класи Message і Inbox трохи зміняться - тепер вони повинні успадковуватися від Outputable, а також зміняться і методи toPage()
Message.php

class Message extends Outputable ( /** * Зміст повідомлення. */ var $content; /** * Конструктор для ініціалізації тексту повідомлення. * * @param content зміст повідомлення */ function Message($content) ( $this->content = $content; ) /** * Запис повідомлення на сесію. ( $this->appendOutput(" ".$this->content.""); } }

class Inbox extends Outputable ( /** * Масив повідомлень, що надійшли. */ var $messages = array(); /** * У конструкторі отримуємо всі повідомлення, що надійшли * і видаляємо їх з сесії. */ function Inbox() ( if (is_array ($_SESSION["session_messages"])) ( $messages = $_SESSION["session_messages"]; $co = sizeof($messages); for ($i = 0; $i< $co; $i++) { $this->messages = новий Message($messages[$i]); ) ) /* очищаємо масив повідомлень */ $_SESSION["session_messages"] = array(); ) /** * Виводимо на сторінку вміст Inbox. */ function toPage() ( $co = sizeof($this->messages); $this->appendOutput(" "); for ($i = 0; $i< $co; $i++) { $this->messages[$i]->toPage(); $this->appendOutput($this->messages[$i]->getOutput()); ) $this->appendOutput(""); } }

Змінився спосіб виведення - тепер замість безпосереднього виведення на сторінку зовнішнє уявлення до певного часу зберігається в Outputable, який "сидить" у кожному з об'єктів. Метод appendOutput() є деякою заміною конструкції echo(). Щоб забрати виведення об'єкта, використовується метод getOutput().

Тепер подивимося, що являє собою клієнтська частина коду, яка вирішуватиме те саме завдання, що й раніше.
index.php

send(); /* поточна секунда */ $msg_sec = new Message("second: ". date("s")); $msg_sec->send(); /* перенаправлення він * * header("location:"); exit; ) else ( /* готуємо список повідомлень у вигляді XML */ $inbox = new Inbox(); $inbox->toPage(); $global_content->appendOutput($inbox->getOutput()); ) $global_content->appendOutput (" "); $xml_string = $global_content->getOutput(); $xh = xslt_create(); $xarg = array(); /* заголовок XML-документу */ $xarg["xml"] = ""."n"; /* тіло XML-документу */ $xarg["xml"] .= " " . $xml_string . "/* XSL-шаблон */ $xarg["xsl"] = implode("", file("style.xsl")); /* виводимо HTML-код - результат XSL-трансформації */ echo xslt_process($xh , "arg:xml", "arg:xsl", NULL, $xarg); /* виводимо XML-вихідник (debug) */ echo "


" . htmlspecialchars($xml_string) . "
"; ?>

Головне нововведення - в об'єкті $global_content, назва якого говорить сама за себе. В даному випадку він належить класу Outputable, у реальних завданнях ви, напевно, створите окремий клас для контенту сторінки.

Якщо уважно придивитися, то змістовна частина скрипта практично не змінилася - той самий inbox, той же toPage(). Додана інструкція, яка містить вміст списку повідомлень у контенті сторінки. Для різноманітності тепер генерується два повідомлення.

Для того щоб подивитися на результат, залишилося лише підготувати XSL-шаблон.
style.xsl

XSLT Example

message

Чого ж ми досягли?

Насамперед, можна сміливіше братися за складні проекти – забезпечено реальну незалежність модулів. Порядок укладання результатів на сторінку контролюється за допомогою зовнішнього XSL-шаблону і не залежить від порядку запуску модулів.

Будь-який модуль, який генерує XML-дані як результат своєї роботи, може бути використаний у проекті. До речі, це одна з переваг перед template-движками, в яких створення даних полягає в послідовності виклику методів (assign і т.п.) конкретного движка, на яких немає загального стандарту.

Ще одна перевага – легкість налагодження. Якщо ви запустите скрипт, то помітите, що на кожній сторінці присутній debug-висновок - XML-прообраз, який дуже спрощує налагодження додатків.

Над чим ще треба подумати - як створювати об'єкти-повідомлення. Не завжди зручно використовувати new безпосередньо в коді клієнта. Але це тема для окремої статті.

Насамкінець, галопом про перспективи:

* спливаючі вікна для списку важливих повідомлень
* "сторінки-відправники" та "сторінки-адресати" у повідомленнях
* ведення лога повідомлень у базі даних
* кнопка "показати історію моїх дій"
* статистичний аналіз дій користувачів у межах сесій
* "інтелектуальні помічники" у веб-додатках