Печём пирожки или начало работы с CakePHP по-русски

CAKE_TEST

Скоро товарищей, пишущих по старинке веб-страницы на голом php, можно будет показывать в зоопарках :) Наступает эра Фреймворков. И это хорошо, потому что программист избавлен от множества рутинных задач, и может сосредоточиться на создании логики приложения. В CakePHP используется подход MCV (Model - View - Controller). Теперь вместо одного файла *.php на одну страничку их будет целых три минимум %) - один для модели (описывает связь формы с базой данных, имеет расширение php), thtml файл будет описывать внешний вид страницы (view), третий (контроллер страницы с расширением php будет реализовывать логику формы и будет содержать методы, дополняющие функциональность родительского класса AppController.

Что ж, начнем печь приложения как пирожки :)) Но для этого придется преступить через себя и принять новый подход к программированию. Оно того стоит ^_^

Установка

Вам нужен Apache с установленным mod_rewrite и mod_php, php4 или php5, а также база данных (в нашем примере используется MySQL). Первым делом разживитесь на www.cakephp.org свежей версией CakePHP. Там же расположен неплохой мануал, которому я во многом буду следовать. Распакуйте архив в корневую директорию веб-сервера и наберите в браузере адрес сайта. Должна появиться приветственная страничка: Your database configuration file is not present.

Нужно прописать соединение с базой данных. Делается это в директории app/config - берем за основу database.php.default и переименовываем его в database.php. Прописываем в файле параметры соединения с базой. Если все ОК то CakePHP сообщит об этом. Your database configuration file is present. Cake is able to connect to the database.

Все готово для творчества.

Модуль новостей

Напишем простейший модуль новостей, чтобы проиллюстрировать технологию создания сайтов на Фреймворке. Создадим в базе данных таблицу posts, в которой будут храниться новости.CREATE TABLE `posts` ( `id` bigint(20) NOT NULL auto_increment, `post_date` date NOT NULL default '0000-00-00', `post_header` varchar(255) NOT NULL default '', `post_text` text NOT NULL, `archived` tinyint(4) NOT NULL default '0', PRIMARY KEY (`id`) )

Вопрос: почему бы таблицу не назвать "news"? Таблица должна иметь имя сущности во множественном числе. В английском "новости" news всегда употребляется во множественном числе, тем более слово new является ключевым в php, что приводит к ошибке.

Модель

В папке app/models создадим файл модели post.php VALID_NOT_EMPTY, 'post_text' => VALID_NOT_EMPTY, 'post_date' => VALID_NOT_EMPTY ); } ?>

В массиве $validate описываются ограничения накладываемые логикой приложения на данные. Мы запретили пустые поля Текст, Заголовок и Дата.

Контроллер

В папке app/controllers создадим файл posts_controller.php. Создадим объект, который реализует основные функции модуля новостей: отображение списка (index), просмотр новости (view), удаление новости (delete), добавления новости (add).set('posts', $this->Post->findAll()); // выборка всех
записей //из таблицы $this->pageTitle = 'Новости'; } function view($id) // просмотр новости { $this->Post->id = $id; $this->set('post', $this->Post->read()); } function add() // добавление новости { if (!empty($this->data)) { if ($this->Post->save($this->data)) { $this->flash('Новость была успешно добавлена','/posts'); // flash выдает сообщение пользователю в виде ссылки на страницу, указанной во втором аргументе функции } } } function delete($id) // удаление новости { $this->Post->del($id); $this->flash('Новость была удалена', '/posts'); } function edit($id = null) // редактирование новости { if (empty($this->data)) { $this->Post->id = $id; $this->data = $this->Post->read(); } else { if ($this->Post->save($this->data['Post'])) { $this->flash('Новость была отредактирована','/posts'); } } } } ?>

Виды

В папке app/views создадим папку posts. В этой папке необходимо создать файлы видов для каждой функции, кроме функции Delete, которая не требует своего вида, а использует только функцию flash.

app/views/posts/index.thtmlНовости >'; if (count($posts)>0) { ?>

ДатаЗаголовокТекст
= $news_teaser_length) { $last_word = $mes_words[$news_teaser_length -1]; for ($i = 0; $i <= $news_teaser_length -1 ; $i++) { $text = $text.$mes_words[$i]." "; } echo $html->link($text.$news_more_annex, '/posts/view/'.$post['Post']['id']); } else { echo $html->link($post['Post']['post_text'], '/posts/view/'.$post['Post']['id']); } ?> link('Редактировать', '/posts/edit/'.$post['Post']['id']); ?> link('Удалить', '/posts/delete/'.$post['Post']['id']); ?>
Новостей пока нет"; } ?>

Добавить новость?

app/views/posts/add.thtmlДобавить новость

Название:
input('Post/post_header', array('size' => '40'))?> tagErrorMsg('Post/post_header', 'Название не может быть пустым.') ?>

Дата:
input('Post/post_date', array('size' => '40'))?> tagErrorMsg('Post/post_data', 'Дата не может быть пустой.') ?>

Текст:
textarea('Post/post_text', array('rows'=>'10')) ?> tagErrorMsg('Post/post_text', 'Сообщение не может быть пустым.') ?>

submit('Отправить') ?>

app/views/posts/edit.thtmlРедактирование новости hidden('Post/id'); ?>

Заголовок:
input('Post/post_header', array('size' => '40'))?> tagErrorMsg('Post/post_header', 'Необходим заголовок.') ?>

Дата:
input('Post/post_date', array('size' => '40'))?> tagErrorMsg('Post/post_date', 'Необходима дата.') ?>

Текст:
textarea('Post/post_text', array('rows'=>'10')) ?> tagErrorMsg('Post/post_text', 'Текст не должен быть пустым!') ?>

submit('Сохранить') ?>

appviewspostsview.thtml

Создана:

Фу: вроде все почти готово :) Теперь осталось перенаправить пользователя c приветственной странички CakePHP на страничку app/posts.

Для этого идем в app/config/routes.php и меняем там строку$Route->connect(’/', array(’controller’ =>
‘pages’, ‘action’ => ‘display’, ‘home’));

на строку$Route->connect (’/', array(’controller’=>’posts’, ‘action’=>’index’));

Теперь можете в браузере набрать адрес сайта и понаблюдать CakePHP в действии :)

Небольшая обработка напильником

Полюбовавшись на полученное приложение, пытливый читатель скоро почувствует разочарование. Как поменять стиль страницы, убрать маленький баннер снизу и гордое "CakePHP Rapid Development" в верху каждой страницы? Как заставить функцию flash отображать текст по-русски? Для этой цели служат layouts. (Долго думал, как перевести layouts и решил остановиться на слове "шаблоны"). Бодро идем в папку app/views/layouts и помещаем там нужные файлы:

default.thtml, например, такой:

Вылечим функцию flash от любви к utf8. Для этого разместим там же flash.thtml

Прототипы этих файлов можно увидеть в папке cake/libs/view/templates/layouts

Вроде бы все :) Прошу php пуристов не критиковать мой код, ибо сделан он больше развлечения ради. Рекомендую сходить на cakeforge.org и скачать там мануал и помощь по API в формате chm. Удачи в печении плюшек!

4 июля 2007 Г.

CakePHP -

CakePHP -

, - php, :) . , , . CakePHP MCV (Model - View - Controller). *.php %) - ( , php), thtml (view), ( php , AppController.

, :)) . ^_^

Apache mod_rewrite mod_php, php4 php5, ( MySQL). www.cakephp.org CakePHP. , . - . :

Your database configuration file is not present.

. app/config - database.php.default database.php. . CakePHP .

Your database configuration file is present.

Cake is able to connect to the database.

.

, . posts, .

CREATE TABLE `posts` (

  `id` bigint(20) NOT NULL auto_increment,

  `post_date` date NOT NULL default '0000-00-00',

  `post_header` varchar(255) NOT NULL default '',

  `post_text` text NOT NULL,

  `archived` tinyint(4) NOT NULL default '0',

  PRIMARY KEY  (`id`)

)

: "news"? . "" news , new php, .

app/models post.php

<?php
class Post extends AppModel

{

    var $name = 'Post';

    var $validate = array(

        'post_header'  => VALID_NOT_EMPTY,

        'post_text'   => VALID_NOT_EMPTY,

        'post_date'   => VALID_NOT_EMPTY

    );

}

?>

$validate . , .

app/controllers posts_controller.php. , : (index), (view), (delete), (add).

<?php

class PostsController extends AppController // 
//  AppController

{

    var $name = 'Posts';

    function index() //     

    {

         $this->set('posts', $this->Post->findAll()); //  
// $this->pageTitle = ''; } function view($id) // { $this->Post->id = $id; $this->set('post', $this->Post->read()); } function add() // { if (!empty($this->data)) { if ($this->Post->save($this->data)) { $this->flash(' ','/posts'); // flash , } } } function delete($id) // { $this->Post->del($id); $this->flash(' ', '/posts'); } function edit($id = null) // { if (empty($this->data)) { $this->Post->id = $id; $this->data = $this->Post->read(); } else { if ($this->Post->save($this->data['Post'])) { $this->flash(' ','/posts'); } } } } ?>

app/views posts. , Delete, , flash.

app/views/posts/index.thtml

<h1></h1>

<?

//   ,  
//   ""  "..."

  $news_teaser_length = 12;

  $news_more_annex = ' >>';

  if (count($posts)>0)

  {

?>

<table>

<tr>

 <th></th>

 <th></th>

 <th></th>

 <th> </th>

 <th> </th>

</tr>

    <?php foreach ($posts as $post): ?>

    <tr>

        <td>

        <?php

          echo $post['Post']['post_date'];

        ?>

        </td>

        <td>

        <?php

        echo $post['Post']['post_header'];

        ?>

        </td>

        <td>

            <?php

            //    
            //(   $news_teaser_length)
            //     $news_teaser_length
            // 

            $line = $post['Post']['post_text'];

            $tmpline = preg_replace("/s+/", " ", $line);

            $mes_words = explode(' ',$tmpline);

            $text = "";

            if (count($mes_words) >= $news_teaser_length)

            {

              $last_word = $mes_words[$news_teaser_length -1];

              for ($i = 0; $i <= $news_teaser_length -1 ; $i++)

              {

                $text = $text.$mes_words[$i]." ";

              }

              echo $html->link($text.$news_more_annex,

                       '/posts/view/'.$post['Post']['id']);

            }

            else

            {

              echo $html->link($post['Post']['post_text'],
                  '/posts/view/'.$post['Post']['id']);

            }

            ?>

        </td>

        <td>

            <?php

            echo $html->link('',
               '/posts/edit/'.$post['Post']['id']);

           ?>

        </td>

        <td>

           <?php

            echo $html->link('',
               '/posts/delete/'.$post['Post']['id']);

           ?>

        </td>

    </tr>

    <?php endforeach; ?>

</table>

<?

}

  else

  {

    echo "<p>  </p>";

  }

?>

 <p><a href="/posts/add"> ?</a></p>

app/views/posts/add.thtml

<h1> </h1>

<form method="post" action="<?php
  echo $html->url('/posts/add')?>">

    <p>

        : <br>

        <?php echo $html->input('Post/post_header',
           array('size' => '40'))?>

        <?php echo $html->tagErrorMsg('Post/post_header',
           '    .') ?>

    </p>

    <p>

        : <br>

        <?php echo $html->input('Post/post_date',
           array('size' => '40'))?>

        <?php echo $html->tagErrorMsg('Post/post_data',
            '    .') ?>

    </p>

    <p>

        :<br>

        <?php echo $html->textarea('Post/post_text',
           array('rows'=>'10')) ?>

        <?php echo $html->tagErrorMsg('Post/post_text',
             '    .') ?>

    </p>

    <p>

        <?php echo $html->submit('') ?>

    </p>

</form>

app/views/posts/edit.thtml

<h1> </h1>

<form method="post" action="<?php
   echo $html->url('/posts/edit')?>">

    <?php echo $html->hidden('Post/id'); ?>

    <p>

        :<br>

        <?php echo $html->input('Post/post_header',
           array('size' => '40'))?>

        <?php echo $html->tagErrorMsg('Post/post_header',
           ' .') ?>

    </p>

    <p>

        :<br>

        <?php echo $html->input('Post/post_date',
           array('size' => '40'))?>

        <?php echo $html->tagErrorMsg('Post/post_date',
           ' .') ?>

    </p>

    <p>

        :<br>

        <?php echo $html->textarea('Post/post_text',
           array('rows'=>'10')) ?>

        <?php echo $html->tagErrorMsg('Post/post_text',
            '    !') ?>

    </p>

    <p>

        <?php echo $html->submit('') ?>

    </p>

</form>

app\views\posts\view.thtml

<h1><?php echo $post['Post']['post_header']?></h1>

<p><small>: <?php
  echo $post['Post']['post_date']?></small></p>

<p><?php echo $post['Post']['post_text']?></p>

: :) c CakePHP app/posts.

app/config/routes.php

$Route->connect(/', array(controller =>
pages, action => display, home));

$Route->connect (/', array(controller=>posts, action=>index));

CakePHP :)

, . , "CakePHP Rapid Development" ? flash -? layouts. ( , layouts ""). app/views/layouts :

default.thtml, , :

<html>

<head>

<META http-equiv="content-type"
content="text/html;charset=windows-1251" />

<title>CAKE_TEST<?php echo $title_for_layout?></title>

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

<link rel="stylesheet" type="text/css" href="/css/cake.generic.css" />

</head>

<body>

            <div id="wrapper">

                        <div id="container">

            <div id="content">

                                      <?php echo $content_for_layout ?>

                                   </div>

                        </div>

            </div>

</body>

</html>

flash utf8. flash.thtml

<html>

<head>

<title><?php echo $page_title?></title>

<META http-equiv="content-type"
content="text/html;charset=windows-1251" />

<?php if(Configure::read() == 0) { ?>

<meta http-equiv="Refresh" content="<?php
  echo $pause?>;url=<?php echo $url?>"/>

<?php } ?>

<style><!--

P { text-align:center; font:bold small sans-serif }

A { color:red}

A:HOVER { text-decoration: underline; color:#44E }

--></style>

</head>

<body>

<p><a href="<?php echo $url?>"><?php
  echo $message?></a></p>

</body>

</html>

cake/libs/view/templates/layouts

:) php , . cakeforge.org API chm. !