Показать сообщение отдельно

  #5  
Старый 20.02.2009, 10:43
Kuzya
Участник форума
Регистрация: 27.04.2008
Сообщений: 224
С нами: 9494306

Репутация: 313
По умолчанию

Для сохранения данных из формы мы создадим метод «album_save» в контроллере и «saveAlbumQuery» в модели. Ничего сложного в них не будет. Первый получит 2 поля от пользователя и передаст второму, который в свою очередь вставит эти данные в БД.

PHP код:
function album_save($album_id)
 {
     
$album_id intval($album_id);
     
// Получаем нужные поля
     
$name   $this->input->post('name',true);
     
$about  $this->input->post('about',true);
     
// Передаём их на изменение
     
$this->Albums_model->saveAlbumQuery($album_id,$name,$about);
     
// Вызываем функцию редактирования изменённого альбома
     
self::album_edit($album_id);

PHP код:
function saveAlbumQuery($album_id,$name,$about)
{
    
// Формируем данные для обновления
    
$data = Array(
        
'name'=>$name,
        
'about'=>$about
    
);
    
// Добавляем условие "WHERE id = '$album_id'"
    
$this->db->where('id',$album_id);
    
// Обновляем таблицу `albums`
    
$this->db->update('albums',$data);

Проверьте их работоспособность. Общая часть редактирования готова.
Теперь стоит добавить в шаблон форму загрузки файлов с двумя полями- для единичных фотографий и для архивов с ними. Не закрывая таблицу вставьте туда ещё одну форму.

Код:
    <form action='/admin/photos/photo_add/{id}/' method='POST' enctype='multipart/form-data'>
        <tr>
            <td width="81" valign="top"><img src="/images/rc.gif" width=55 height=24 alt=""></td>
            <td>
                <span class='stylesbold'>Добавить фотографию:</span>
            </td>
            <td>
                <span class='stylesbold'><input name='photo' type='file' /></span>
            </td>
        </tr>
        <tr>
            <td width="81" valign="top"><img src="/images/rc.gif" width=55 height=24 alt=""></td>
            <td>
                <span class='stylesbold'>Добавить архив фотографий:</span>
            </td>
            <td>
                <span class='stylesbold'><input name='archive' type='file' /></span>
            </td>
        </tr>
        <tr>
            <td width="81" valign="top"><img src="/images/rc.gif" width=55 height=24 alt=""></td>
            <td colspan='2'>
                <input type='submit' value='Добавить' />
            </td>
        </tr>
    </form>
Данные она будет передавать контроллеру «photos». Создадите его с пустым методом «photo_add».

PHP код:
class Photos extends Controller
{
    function 
photo_add($album_id)
    {
        
    }

В этом методе мы будем получать данные от формы, производить загрузку требуемых файлов и записывать в базу данные загруженных изображений. Сначала мы напишем загрузку фотографий по отдельности, а под конец и архивом. Но перед этим создадим модель «Photos_model» которая будет содержать метод регистрации фотографии в базе. В нем мы сначала сформируем нужные для вставки данные, а затем вставим с помощью метода «insert». Называться этот метод будет «insertPhoto» и передавать ему нужно будет 2 параметра — имя файла фотографии и номер альбома, которому она принадлежит.

PHP код:
function insertPhoto($file_name,$album_id)
{
    
// Формируем массив данных для вставки
    
$data = Array(
        
'id'       => '',
        
'album_id' => $album_id,
        
'file_name'=> $file_name
    
);

    
$this->db->insert('photos',$data);

Примемся за контроллер. Сначала добавим в него загрузку нашей модели в конструкторе.
PHP код:
function __construct()
{
    
parent::Controller();

    
$this->load->model('Photos_model');

В методе «photo_add» мы с помощью библиотеки «File upload» загрузим фотографию в папку «photos», зарегистрируем новое изображение в базе, и с помощью функции «redirect», хэлпера «URL», мы вернём пользователя обратно к редактированию альбома. Обратите внимание на то что для загрузки файлов библиотеке «File upload» нужно передать 2 обязательных парметра — путь загрузки и список допустимых расширений. Список расширений передаётся не в виде массива а в формате «расширение|расширение|рас ширение». В этот раз мы инициализируем настройки класса немного по другому. Возьмём прошлый пример с использованием класса «Pagination». Сначала мы загрузили библиотеку, потом сформировали данные конфигурации для неё и только потом, с помощью метода «initialize» загрузили в неё эти данные. Сейчас же мы передадим конфигурационные данные вторым возможным способом — при загрузке библиотеки. Для этого их нужно вторым параметром передать методу «library». При таком способе у нас не будет нужды писать строку с вызовом метода «initialize».

PHP код:
    // Формируем настройки для класса "File Upload"
    
$upload_config['upload_path']   = './photos/';
    
$upload_config['allowed_types'] = 'gif|jpg|png';
    
// Передаём их при загрузке библиотеки
    
$this->load->library('upload',$upload_config); 

Непосредственно загрузка осуществляется методом «do_upload» которому нужно передать имя поля для выбора файла. У нас их 2 — «photo» и «archive», но пока мы будем работать лишь с первым. Если загрузка пройдёт успешно то мы запишем информацию о загруженном файле в базу. В конце мы просто перенесём пользователя обратно на редактирование альбома.

PHP код:
function photo_add($album_id)
{

    
$album_id intval($album_id);

    
$this->load->helper('url');
    
// Формируем настройки для класса "File Upload"
    
$upload_config['upload_path']   = './photos/';
    
$upload_config['allowed_types'] = 'gif|jpg|png';
    
// Передаём их при загрузке библиотеки
    
$this->load->library('upload',$upload_config);
    
// Пробуем загрузить фото
    
if($this->upload->do_upload('photo'))
    {
        
// Получаем данные загруженного файла
        
$file_data $this->upload->data();
        
// Регистрируем фотографию в базе
        
$this->Photos_model->insertPhoto($file_data['file_name'],$album_id);
    }
    
// Возвращаем пользователя к редактированию альбома.
    
redirect("/admin/albums/album_edit/{$album_id}/");

Если Вы попытаетесь сейчас загрузить фотографию то заметите что после загрузки URL, на который Вас перенесло, содержит «index.php». Функция «redirect» сама его подставляет. Что бы он не заносился в URL при редиректах откройте файл «application/config.php» и сотрите «index.php» в параметре «index_page», оставив там пустую строку.
Теперь нам нужно написать отображение всех, существующих в альбоме, фотографий. Исходными данными для этого будет массив номеров фотографий текущего альбома, полученный из БД. Его формированием будет заниматься метод «getPhotosArray».

PHP код:
function getAlbumPhotosArray($album_id)
{
    
// Указываем что из всех колонок
    // таблицы нам нужна только одна - id
    
$this->db->select('id');
    
// Устанавливаем WHERE-условие
    
$this->db->where('album_id',$album_id);
    
$query $this->db->get('photos');
    
// Обрабатываем результат
    
$photos_ids = Array();
    foreach(
$query->result_array() as $row)
        
$photos_ids[] = Array('photo_id'=>$row['id']);

    return 
$photos_ids;

Вас может удивить код

PHP код:
$photos_ids[] = Array('photo_id'=>$row); 
Формирование массива для каждого номера нужно для последующей правильной обработки основного массива(«photos_ids») в шаблоне. А в контроллер мы добавим всего одну строку которая будет создавать новую ячейку в массиве информации о альбоме:

PHP код:
$album $this->Albums_model->getAlbumPhotosArray($album_id); 
Теперь стоит немного подправить шаблон редактирования и вставить туда код отображения картинок. Сделаем это сразу после всех форм.

Код:
<table width='100%' align='center'>
<tr>
    {photos}
    <td>
        <img src='/admin/photos/photo_view/{photo_id}/150/' /><br /><br />
        <a href='/admin/photos/photo_del/{photo_id}/{id}/'>Удалить</a>
    </td>
    {/photos}
</tr>
</table>
Если Вы сейчас посмотрите на получившийся список фотографий то ничего не обнаружите потому что у нас нет метода обработки изображений.
Для работы с изображениями в CI имеется библиотека «image_lib» (http://code-igniter.ru/user_guide/libraries/image_lib.html). Список возможностей у неё очень большой, но мы рассмотрим лишь изменение размера изображений. Из шаблона видно что метод, на который ссылается тег «img», называется «photo_view». В нём мы будем изменять размер требуемого изображения и выдавать его в браузер. Для нормальной работы методу будут передаваться 2 параметра — номер фотографии в базе и число 150. Это ширина будущего изображения. Высота же будет генерироваться относительно ширины.
Так как методу изменения размера изображения нужно передавать имя файла, а у нас на входе имеется только её номер в базе, то придётся расширить функционал модели «Photos_model». В ней мы создадим функцию получающую данные из базы о фотографии по её номеру. Код её достаточно прост, и в комментариях врятли нуждается.

PHP код:
function getPhotoData($photo_id)
{
    
// Установка условия
    
$this->db->where('id',$photo_id);
    
$query $this->db->get('photos');
    
$row $query->result_array();

    return 
$row[0];

В самом методе отображения мы сначала получим данные из базы, потом укажем нужные для ресайза данные и с помощью функции «resize» изменим размер изображения. При инициализации библиотеки «image_lib» нужно указать список параметров которые будут использоваться при работе с изображением. У нас этот список будет следующий:
source_image — полный путь к фотографии.
maintain_ratio — будет установлен в «true» для того что бы при изменении размеров соблюдались пропорции.
width — ширина будущего изображения.
height — высота будущего изображения. Если её не указать то в изображении изменится только ширина, невзирая на сохранении пропорций.
dynamic_output — будет установлен в «true» для того что бы результат работы выводился в браузер сразу. Иначе изображение будет сохранено на жёстком диске.

PHP код:
function photo_view($photo_id,$width 100)
{
    
$photo_id intval($photo_id);
    
$width    intval($width);

    
$this->load->library('Image_lib');
    
// Получение данных из БД
    
$photo $this->Photos_model->getPhotoData($photo_id);
    
// Составляем полный путь к картинке из корня приложения
    
$path  './photos/'.$photo['file_name'];
    
// Формируем данные для изменения размера
    
$config['source_image'] = $path;
    
$config['maintain_ratio'] = true;
    
$config['width'] = $width
    
$config['height'] = 100;
    
$config['dynamic_output'] = TRUE;

    
$this->image_lib->initialize($config);
    
// Изменяем размер и выводим в браузер результат
    
$this->image_lib->resize();

Вот и всё. Теперь при просмотре формы редактирования фотоальбома Вы будете видеть уменьшенные копии изображений хранящихся в нём.
Время поработать над пакетной закачкой. В базовом наборе CI есть библиотека «Zip Encoding», но она ориентирована лишь на создание архивов, а не на их распаковку. Для того что бы научить CI распаковывать архивы мы подключим к нему библиотеку PclZip. Вы можете скачать её последнюю версию здесь: http://www.phpconcept.net/pclzip/index.en.php . Сохраните скачанный файл «pclzip.lib.php» в папке пользовательских классов «application/libraries», но с именем «Pclzip.php». Затем откройте этот скрипт. В начале файла Вы увидите большое количество констант устанавливаемых для работы данной библиотеки. Что бы соответствовать стандартам перенесём всё объявление констант в файл «application/config/constants.php», тут содержаться константы объявляемые при старте CI. В файле библиотеки должен остаться только код класса (объявление единственной внеклассовой переменной «g_pclzip_version» можно просто удалить). Теперь переименуйте имя класса и конструктора из «PclZip» в «Pclzip». И последним нашим действием замените в конструкторе объявление свойства «zipname» с
PHP код:
$this->zipname $p_zipname
на

PHP код:
$this->zipname $p_zipname
Это нужно вот зачем. Конструктор PclZip требует что бы при создании объекта (а он создастся сразу после загрузки библиотеки) был передан параметр «p_zipname», содержащий путь к архиву с которым мы будем работать. Данные конструктору любой библиотеки в CI передаются при её загрузке массивом (мы работали так с библиотекой «File upload» выше, передавая конструктору массив «upload_config»). Под массив параметров в конструкторе приспособлены в CI все библиотеки, а вот PclZip ждёт в конструкторе строку. Что бы всё нормально работало мы в конструктор передадим массив, а потом имя файла просто достанем из ячейки «p_zipname». Будьте внимательны с этим и в своих библиотеках — не указывайте в конструкторе передачу нескольких параметров, параметры должны передаваться одним массивом. Теперь данную библиотеку можно спокойно загружать и работать с ней. Маленькое отступление. Создайте в корне приложения папку «temp». В неё мы будем загружать архивы, распаковывать их и только потом распакованные фотографии будут копироваться в общую папку.
Перейдём к коду самого метода. Для наших целей мы расширим метод «photo_add» контроллера «Photos». Сразу после части кода отвечающей за загрузку одной фотографии мы заново инициализируем библиотеку «File upload» уже с другими параметрами и попытаемся загрузить отправленный архив.

PHP код:
// Составляем новые настройки
$upload_config = Array();
// Меняем папку загрузки
$upload_config['upload_path']   = './temp/';
// Меняем разрешённые расширения
$upload_config['allowed_types'] = 'zip';
// Заново инициализируем библиотеку
$this->upload->initialize($upload_config);
// Пытаемся загрузить архив
if($this->upload->do_upload('archive'))
{
    
//... Код обработки архива

Алгоритм обработки архива будет следующий. Сначала мы с помощью метода «data» получим информацию о загруженном файле. Затем загружаем библиотеку PclZip и с помощью метода «extract» распаковываем содержимое архива в папку «temp». Далее мы удалим архив для того чтобы в папке остались одни фотографии и перебирая их циклом по очереди скопируем в папку изображений фотоальбомов и зарегистрируем в базе.

PHP код:
// Получаем данные о загруженом файле
$file_data $this->upload->data();
// Параметры для PclZip
$pclzip_config = Array();
$pclzip_config['p_zipname'] = './temp/'.$file_data['file_name'];
$this->load->library('Pclzip',$pclzip_config);
// Распаковываем архив
$this->pclzip->extract('./temp/');
// И тут же его удаляем
unlink('./temp/'.$file_data['file_name']);
// Получаем список фотографий
$directory directory_map('./temp/');
// и циклично их обрабатываем
foreach($directory as $photo)
{
    
// Копируем в директорию фотоальбомов
    
copy("./temp/{$photo}","./photos/{$photo}");
    
// Регистрируем в БД
    
$this->Photos_model->insertPhoto($photo,$album_id);
    
// И удаляем из временной директории
    
unlink("./temp/{$photo}");

Обратите внимание на то что список файлов формируется с использованием функции «directory_map» хэлпера «Directory», а в начале функции у нас подключается только хэлпер «url». Добавьте загрузку хэлпера «Directory». В итоге код метода «photo_add» должен стать таким.

PHP код:
function photo_add($album_id)
{
    
$album_id intval($album_id);

    
$this->load->helper(Array('url','directory'));
    
// Формируем настройки для класса "File Upload"
    
$upload_config['upload_path']   = './photos/';
    
$upload_config['allowed_types'] = 'gif|jpg|png';
    
// Передаём их при загрузке библиотеки
    
$this->load->library('upload',$upload_config);
    
// Пробуем загрузить фото
    
if($this->upload->do_upload('photo'))
    {
        
// Получаем данные загруженного файла
        
$file_data $this->upload->data();
        
// Регистрируем фотографию в базе
        
$this->Photos_model->insertPhoto($file_data['file_name'],$album_id);
    }

    
// Составляем новые настройки
    
$upload_config = Array();
    
// Меняем папку загрузки
    
$upload_config['upload_path']   = './temp/';
    
// Меняем разрешённые расширения
    
$upload_config['allowed_types'] = 'zip';
    
// Заново инициализируем библиотеку
    
$this->upload->initialize($upload_config);
    
// Пытаемся загрузить архив
    
if($this->upload->do_upload('archive'))
    {
        
// Получаем данные о загруженом файле
        
$file_data $this->upload->data();
        
// Параметры для PclZip
        
$pclzip_config = Array();
        
$pclzip_config['p_zipname'] = './temp/'.$file_data['file_name'];
        
$this->load->library('Pclzip',$pclzip_config);
        
// Распаковываем архив
        
$this->pclzip->extract('./temp/');
        
// И тут же его удаляем
        
unlink('./temp/'.$file_data['file_name']);
        
// Получаем список фотографий
        
$directory directory_map('./temp/');
        
// и циклично их обрабатываем
        
foreach($directory as $photo)
        {
                
// Копируем в директорию фотоальбомов
                
copy("./temp/{$photo}","./photos/{$photo}");
                
// Регистрируем в БД
            
$this->Photos_model->insertPhoto($photo,$album_id);
            
// И удаляем из временной директории
            
unlink("./temp/{$photo}");
        }
    }

    
// Возвращаем пользователя к редактированию альбома.
    
redirect("/admin/albums/album_edit/{$album_id}/");

Попытайтесь сейчас сформировать архив из 3-4 фотографий и загрузить его.
На очереди удаление. Оно будет происходить так же просто как и у фотоальбомов. В контроллере «Photos» создайте метод «photo_del» с двумя параметрами передаваемыми по ссылке — номера фотографии и альбома. Номер альбома нам нужен для того что бы после выполнения удаления перенаправить пользователя обратно на редактирование.
PHP код:
function photo_del($photo_id,$album_id)
{
    
// Получаем номер фотографии и номер её альбома
    
$photo_id intval($photo_id);
    
$album_id intval($album_id);
    
// Получаем её данные что бы знать имя файла
    
$photo $this->Photos_model->getPhotoData($photo_id);

    
$this->load->helper('url');
    
// Удаляем из базы запись о фотографии
    
$this->Photos_model->photo_del($photo_id);
    
// Удаляем её из папки 'photos'
    
unlink('./photos/'.$photo['file_name']);
    
// Переносим пользователя на редактирование альбома.
    
redirect('/admin/albums/album_edit/'.$album_id);

Затем создадим функцию «photo_del», осуществляющую непосредственно удаление из базы, в модели фотографий. Она будет ещё проще:

PHP код:
function photo_del($photo_id)
{
    
$this->db->delete('photos',Array('id'=>$photo_id));

Это всё. Попробуйте для проверки удалить пару фотографий.
Предпоследним действием в отношении админ-панели у нас будет создание бэкапа фотографий. Данная функция будет формировать ZIP-архив со всеми имеющимися фотографиями на сервере, и передавать его на закачку браузеру. Внимание! Никогда не делайте похожих вещей в своих проектах так как они просто не будут работать на большинстве серверов. Дело в том что на многих серверах (а тем более на различных хостингах) объём памяти, выделяемой для PHP, сильно ограничен. Как правило это не больше 25 мегабайт. Соответственно, если объём архива будет превышать это ограничение то будет вылазить ошибка гласящая о недостатке памяти. Ведь архив перед выдачей пользователю (или сохранением на жёсткий диск) формируется именно в памяти. Создание такой функции мы рассматриваем здесь только для того что бы познакомиться с некоторыми свойствами библиотеки «Zip Encoding»(http://code-igniter.ru/user_guide/libraries/zip.html), использование которой может быть Вам полезно при выполнении каких-либо других задач. Итак, начнём. Метод, ответственный за создание резервных копий, будет называться «photos_backup» и находиться в модели «Photos». В начале метода будут загружаться 2 компонента — хэлпер directory (с помощью его функции мы получим список файлов для архивирования) и библиотека «zip». Далее мы получим список файлов и по очереди добавим их в архив. В конце метода мы отдадим содержимое архива браузеру пользователя. Это может Вас удивить, но код этой функции крайне мал.

PHP код:
function photos_backup()
{
    
$this->load->helper('directory');
    
$this->load->library('zip');
    
// Получаем список файлов в папке «photos»
    
$files directory_map('./photos/');
    
// Поочерёдно добавляем их в архив
    
foreach($files as $file)
        
$this->zip->read_file('./photos/'.$file);
    
// Выдаём архив на закачку
    
$this->zip->download('backup.zip');

При прохождении через цикл мы используем метод «read_file». Он считывает указанный файл и сразу же помещает его в архив. С тем же успехом мы могли бы использовать и вторую подобную функцию - «add_data». Ей нужно передать 2 параметра — имя добавляемого файла и его содержимое. Перевести нашу функцию на использование «add_file» можно заменив строку

PHP код:
$this->zip->read_file('./photos/'.$file); 
на

PHP код:
$this->zip->add_data($file,file_get_contents('./photos/'.$file)); 
Согласитесь, первый вариант удобнее. Обратите внимание ещё на то что для добавления бинарных файлов методом «add_data» нужно использовать функции бинарно-безопасного чтения (или как их ещё называют — функции для безопасного чтения двоичных данных) их содержимого. В конце метода за место вызова закачки файла можно его сохранить на жёсткий диск методом archive, передав ему путь и имя будущего архива.

PHP код:
$this->zip->archive('./backup.zip'); 
Как завершающую часть администраторской панели сделаем авторизацию. Алгоритм её работы будет таков. При каждом обращении к нашему приложению (не важно к какой части) будет происходить проверка — обращается пользователь в администраторскую часть сайта или нет. Если да то следующим шагом будет проверка данных авторизации. Мы их будем записывать в сессию. Если в сессии таких данных нет, то показываем форму ввода пароля, или же, если пароль введён сверяем его с настоящим (пароль у нас будет 123) и в случае совпадения записываем в сессию данные об успешной авторизации. Сделаем мы всё это с помощью класса для работы с сессиями (http://code-igniter.ru/user_guide/libraries/sessions.html) и хуков ядра (http://code-igniter.ru/user_guide/general/hooks.html). Хуки мы будем использовать для того чтобы запустить нашу проверку перед выполнением каких-либо действий контроллером. Активировать хуки можно в основном конфигурационном файле «application/config/config.php», установив значение ячейки «enable_hooks» в значение «true». Так же в этом файле нам нужно установить в «true» значение ячейки «sess_encrypt_cookie» для того что бы используемые сессией cookies шифровались. Дело в том что данные сессии CI хранит в cookies, что с моего взгляда большой промах. Всё-таки лучше лучше когда они хранятся на стороне сервера. Но раз уж так сложилось то будем пользоваться шифрованием данных для того что бы пользователь не имел возможности подделывать их. Далее откроем конфигурацию хуков («application/config/hooks.php») и объявим один:

PHP код:
$hook['post_controller_constructor'] = Array(
    
'class'    => '',
    
'function' => 'admin_auth',
    
'filename' => 'admin_auth.php',
    
'filepath' => 'hooks',
    
'params'   => array()
); 
Мы используем именно хук активизирующийся после конструктора контроллера потому что более ранние хуки не дают доступа к библиотекам и прочим необходимым вещам. Как видно из параметров, файл с ним называется «admin_auth.php». Создадим его в папке хуков «application/hooks» и впишем туда следующую функцию.

PHP код:
function admin_auth()
{
    
// Создаём ссылку на супер объект CI
    // Дальше можно работать с этой ссылкой
    // как с $this в контроллерах и моделях
    
$CI = & get_instance();
    
// Получаем первый сегмент введённого URI
    
$segment $CI->uri->segment(1);
    
// Если первый сегмент URL не "admin" то завершаем работу функции
    
if($segment != 'admin') return ;
    
// Если пользователь не авторизирован
    
if($CI->session->userdata('logged_in') === false)
    {
        
// Если введён верный пароль то записываем в сессию
        // данные о том что пользователь вошёл
        
if($CI->input->post('password') == '123')
        {
            
$CI->session->set_userdata('logged_in',true);
        } else {
            
// Если нет то показываем форму
            
$code  $CI->parser->parse('header',Array(),true);
            
$code .= $CI->parser->parse('admin/admin_login_form',Array(),true);
            
$code .= $CI->parser->parse('footer',Array(),true);
            echo 
$code;
            exit;
        }
    }