Фотогалерея с комментариями

Задумывались ли вы о собственном стиле фотогалереи с комментариями например как на facebook? Основная идея — когда мы нажимаем на изображение — оно всплывает (Ajax) с разделом для комментариев. Все изображения хранятся в базе данных MySQL. И конечно мы будем использовать PHP для достижения нашей цели. Кроме того, система комментариев не добавит более 1-го комментария в течении 10 минут (во избежание спама).

Оглавление
  1. Фотогалерея с комментариями как на Facebook
  2. PHP для фотогалереи
  3. JavaScript для фотогалереи
  4. CSS для фотогалереи

Демо

Второй вариант фотогалереи с комментариями:

Демо

Фотогалерея с комментариями как на Facebook

Для фотогалереи нужны две таблицы: первая таблица содержит записи о изображениях. И несколько полей: название, имя файла, описание, время добавления и количество комментарии. В другую таблицу сохраняются комментарии.

Выполним запрос SQL что бы создать таблицы в базе данных:

CREATE TABLE IF NOT EXISTS `s281_photos` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `title` varchar(255) default '',
  `filename` varchar(255) default '',
  `description` text NOT NULL,
  `when` int(11) NOT NULL default '0',
  `comments_count` int(11) NOT NULL default '0',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO `s281_photos` (`title`, `filename`, `description`, `when`) VALUES
('Item #1', 'photo1.jpg', 'Description of Item #1', UNIX_TIMESTAMP()),
('Item #2', 'photo2.jpg', 'Description of Item #2', UNIX_TIMESTAMP()+1),
('Item #3', 'photo3.jpg', 'Description of Item #3', UNIX_TIMESTAMP()+2),
('Item #4', 'photo4.jpg', 'Description of Item #4', UNIX_TIMESTAMP()+3),
('Item #5', 'photo5.jpg', 'Description of Item #5', UNIX_TIMESTAMP()+4),
('Item #6', 'photo6.jpg', 'Description of Item #6', UNIX_TIMESTAMP()+5),
('Item #7', 'photo7.jpg', 'Description of Item #7', UNIX_TIMESTAMP()+6),
('Item #8', 'photo8.jpg', 'Description of Item #8', UNIX_TIMESTAMP()+7),
('Item #9', 'photo9.jpg', 'Description of Item #9', UNIX_TIMESTAMP()+8),
('Item #10', 'photo10.jpg', 'Description of Item #10', UNIX_TIMESTAMP()+9);

CREATE TABLE IF NOT EXISTS `s281_items_cmts` (
  `c_id` int(11) NOT NULL AUTO_INCREMENT ,
  `c_item_id` int(12) NOT NULL default '0',
  `c_ip` varchar(20) default NULL,
  `c_name` varchar(64) default '',
  `c_text` text NOT NULL ,
  `c_when` int(11) NOT NULL default '0',
  PRIMARY KEY (`c_id`),
  KEY `c_item_id` (`c_item_id`)
) ENGINE=MYISAM DEFAULT CHARSET=utf8;
К началу

PHP для фотогалереи

Теперь создайте файл index.php с кодом:

<?php
// disable warnings
if (version_compare(phpversion(), "5.3.0", ">=")  == 1)
  error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
  error_reporting(E_ALL & ~E_NOTICE);

require_once('classes/CMySQL.php'); // include service classes to work with database and comments
require_once('classes/CMyComments.php');

if ($_POST['action'] == 'accept_comment') {
    echo $GLOBALS['MyComments']->acceptComment();
    exit;
}

// prepare a list with photos
$sPhotos = '';
$aItems = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_photos` ORDER by `when` ASC"); // get photos info
foreach ($aItems as $i => $aItemInfo) {
    $sPhotos .= '<div class="photo"><img src="images/thumb_'.$aItemInfo['filename'].'" id="'.$aItemInfo['id'].'" /><p>'.$aItemInfo['title'].' item</p><i>'.$aItemInfo['description'].'</i></div>';
}

?>

Ниже добавим разметку HTML:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Фотогалерея с комментариями как на Facebook</title>

    <!-- Ссылка стили -->
    <link href="css/main.css" rel="stylesheet" type="text/css" />

    <!-- Ссылка скрипты -->
<script src="https://www.google.com/jsapi"></script>
<script>
        google.load("jquery", "1.7.1");
    </script>
<script src="js/script.js"></script>
</head>
<body>

    <!-- Контейнер с фото -->
    <div class="container">
        <h1>Фото:</h1>
        <?= $sPhotos ?>
    </div>

    <!-- Скрытый блок просмотра -->
    <div id="photo_preview" style="display:none">
        <div class="photo_wrp">
            <img class="close" src="images/close.gif" />
            <div style="clear:both"></div>
            <div class="pleft">тест 1</div>
            <div class="pright">тест 2</div>
            <div style="clear:both"></div>
        </div>
    </div>
</body></html>

Мы только что создали главный файл индекса в фотогалерее. По умолчанию — скрипт создает список изображений (с названием и описанием), а также создается пустой скрытый объект, который мы будем использовать для того чтобы принять пользовательский контент на запросы Ajax. Кроме того, когда мы публикуем комментарии, мы ожидаем этот запрос (принять новый комментарий) в классе комментариев.

Теперь давайте посмотрим следующий важный файл photos_ajx.php:

<?php
// disable warnings
if (version_compare(phpversion(), "5.3.0", ">=")  == 1)
  error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
  error_reporting(E_ALL & ~E_NOTICE);

if ($_POST['action'] == 'get_info' && (int)$_POST['id'] > 0) {

    require_once('classes/CMySQL.php'); // include service classes to work with database and comments
    require_once('classes/CMyComments.php');

    // get photo info
    $iPid = (int)$_POST['id'];
    $aImageInfo = $GLOBALS['MySQL']->getRow("SELECT * FROM `s281_photos` WHERE `id` = '{$iPid}'");

    // prepare last 10 comments
    $sCommentsBlock = $GLOBALS['MyComments']->getComments($iPid);

    $aItems = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_photos` ORDER by `when` ASC"); // get photos info

    // Prev & Next navigation
    $sNext = $sPrev = '';
    $iPrev = (int)$GLOBALS['MySQL']->getOne("SELECT `id` FROM `s281_photos` WHERE `id` < '{$iPid}' ORDER BY `id` DESC LIMIT 1");
    $iNext = (int)$GLOBALS['MySQL']->getOne("SELECT `id` FROM `s281_photos` WHERE `id` > '{$iPid}' ORDER BY `id` ASC LIMIT 1");
    $sPrevBtn = ($iPrev) ? '<div class="preview_prev" onclick="getPhotoPreviewAjx(\''.$iPrev.'\')"><img src="images/prev.png" alt="prev" /></div>' : '';
    $sNextBtn = ($iNext) ? '<div class="preview_next" onclick="getPhotoPreviewAjx(\''.$iNext.'\')"><img src="images/next.png" alt="next" /></div>' : '';

    require_once('classes/Services_JSON.php');
    $oJson = new Services_JSON();
    header('Content-Type:text/javascript');
    echo $oJson->encode(array(
        'data1' => '<img class="fileUnitSpacer" src="images/'. $aImageInfo['filename'] .'">' . $sPrevBtn . $sNextBtn,
        'data2' => $sCommentsBlock,
    ));
    exit;
}

Этот файл отправляет запрос к изображению и оно увеличивается, появится блок с комментариями и навигационные кнопки (открыть предыдущее/следующее изображение AJAXy).

Класс для комментариев classes/CMyComments.php:

<?php

class CMyComments {

    // constructor
    function CMyComments() {
    }

    // return comments block
    function getComments($i) {
        // draw last 10 comments
        $sComments = '';
        $aComments = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_items_cmts` WHERE `c_item_id` = '{$i}' ORDER BY `c_when` DESC LIMIT 10");
        foreach ($aComments as $i => $aCmtsInfo) {
            $sWhen = date('F j, Y H:i', $aCmtsInfo['c_when']);
            $sComments .= <<<EOF
<div class="comment" id="{$aCmtsInfo['c_id']}">
    <p>Comment from {$aCmtsInfo['c_name']} <span>({$sWhen})</span>:</p>
    <p>{$aCmtsInfo['c_text']}</p>
</div>
EOF;
        }

        return <<<EOF
<div class="comments" id="comments">
    <h2>Комментарий</h2>
    <div id="comments_warning1" style="display:none">Не забудьте заполнить оба поля (Имя и Комментарий)</div>
    <div id="comments_warning2" style="display:none">Вы не можете отправлять больше, чем один комментарий за 10 минут (защита от спама)</div>
    <form onsubmit="return false;">
        <table>
            <tr><td class="label"><label>Ваше имя: </label></td><td class="field"><input type="text" value="" title="Please enter your name" id="name" /></td></tr>
            <tr><td class="label"><label>Комментарии: </label></td><td class="field"><textarea name="text" id="text"></textarea></td></tr>
            <tr><td class="label">&nbsp;</td><td class="field"><button onclick="submitComment({$i}); return false;">Пост с комментарием</button></td></tr>
        </table>
    </form>
    <div id="comments_list">{$sComments}</div>
</div>
EOF;
    }

    function acceptComment() {
        $iItemId = (int)$_POST['id']; // prepare necessary information
        $sIp = $this->getVisitorIP();
        $sName = $GLOBALS['MySQL']->escape(strip_tags($_POST['name']));
        $sText = $GLOBALS['MySQL']->escape(strip_tags($_POST['text']));

        if ($sName && $sText) {
            // check - if there is any recent post from you or not
            $iOldId = $GLOBALS['MySQL']->getOne("SELECT `c_item_id` FROM `s281_items_cmts` WHERE `c_item_id` = '{$iItemId}' AND `c_ip` = '{$sIp}' AND `c_when` >= UNIX_TIMESTAMP() - 600 LIMIT 1");
            if (! $iOldId) {
                // if everything is fine - allow to add comment
                $GLOBALS['MySQL']->res("INSERT INTO `s281_items_cmts` SET `c_item_id` = '{$iItemId}', `c_ip` = '{$sIp}', `c_when` = UNIX_TIMESTAMP(), `c_name` = '{$sName}', `c_text` = '{$sText}'");
                $GLOBALS['MySQL']->res("UPDATE `s281_photos` SET `comments_count` = `comments_count` + 1 WHERE `id` = '{$iItemId}'");

                // and print out last 10 comments
                $sOut = '';
                $aComments = $GLOBALS['MySQL']->getAll("SELECT * FROM `s281_items_cmts` WHERE `c_item_id` = '{$iItemId}' ORDER BY `c_when` DESC LIMIT 10");
                foreach ($aComments as $i => $aCmtsInfo) {
                    $sWhen = date('F j, Y H:i', $aCmtsInfo['c_when']);
                    $sOut .= <<<EOF
<div class="comment" id="{$aCmtsInfo['c_id']}">
    <p>Comment from {$aCmtsInfo['c_name']} <span>({$sWhen})</span>:</p>
    <p>{$aCmtsInfo['c_text']}</p>
</div>
EOF;
                }
                return $sOut;
            }
        }
        return 1;
    }

    // get visitor IP
    function getVisitorIP() {
        $ip = "0.0.0.0";
        if( ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) && ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } elseif( ( isset( $_SERVER['HTTP_CLIENT_IP'])) && (!empty($_SERVER['HTTP_CLIENT_IP'] ) ) ) {
            $ip = explode(".",$_SERVER['HTTP_CLIENT_IP']);
            $ip = $ip[3].".".$ip[2].".".$ip[1].".".$ip[0];
        } elseif((!isset( $_SERVER['HTTP_X_FORWARDED_FOR'])) || (empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
            if ((!isset( $_SERVER['HTTP_CLIENT_IP'])) && (empty($_SERVER['HTTP_CLIENT_IP']))) {
                $ip = $_SERVER['REMOTE_ADDR'];
            }
        }
        return $ip;
    }
}

$GLOBALS['MyComments'] = new CMyComments();

?>

Этот класс выполняет две основные функции: принимает новые комментарии, а также он покажет нам окно с комментариями. Есть еще два класса обслуживания: CMySQL.php и Services_JSON.php. Это два известные классы для работы с базой данных и JSON. Вы можете настроить параметры базы данных в базу данных класса. Оба класса в нашей пакете.

К началу

JavaScript для фотогалереи

Теперь мы должны подготовить поведение пользовательского интерфейса с использованием JavaScript в файле js/script.js.

// close photo preview block
function closePhotoPreview() {
    $('#photo_preview').hide();
    $('#photo_preview .pleft').html('empty');
    $('#photo_preview .pright').html('empty');
};

// display photo preview block
function getPhotoPreviewAjx(id) {
    $.post('photos_ajx.php', {action: 'get_info', id: id},
        function(data){
            $('#photo_preview .pleft').html(data.data1);
            $('#photo_preview .pright').html(data.data2);
            $('#photo_preview').show();
        }, "json"
    );
};

// submit comment
function submitComment(id) {
    var sName = $('#name').val();
    var sText = $('#text').val();

    if (sName && sText) {
        $.post('index.php', { action: 'accept_comment', name: sName, text: sText, id: id },
            function(data){
                if (data != '1') {
                    $('#comments_list').fadeOut(1000, function () {
                        $(this).html(data);
                        $(this).fadeIn(1000);
                    });
                } else {
                    $('#comments_warning2').fadeIn(1000, function () {
                        $(this).fadeOut(1000);
                    });
                }
            }
        );
    } else {
        $('#comments_warning1').fadeIn(1000, function () {
            $(this).fadeOut(1000);
        });
    }
};

// init
$(function(){

    // onclick event handlers
    $('#photo_preview .photo_wrp').click(function (event) {
        event.preventDefault();

        return false;
    });
    $('#photo_preview').click(function (event) {
        closePhotoPreview();
    });
    $('#photo_preview img.close').click(function (event) {
        closePhotoPreview();
    });

    // display photo preview ajaxy
    $('.container .photo img').click(function (event) {
        if (event.preventDefault) event.preventDefault();

        getPhotoPreviewAjx($(this).attr('id'));
    });
})

Обратите внимание, что для сценария мы используем jQuery.

К началу

CSS для фотогалереи

В долгосрочной перспективе, мы должны добавить стили к элементам страницы (наш контейнер с фотографиями, фото области предварительного просмотра с комментариями).

Стили для файла css/main.css:

/* project styles */
.container {
    border: 1px solid #111111;
    color: #000000;
    margin: 20px auto;
    overflow: hidden;
    padding: 15px;
    position: relative;
    text-align: center;
    width: 1090px;
    border-radius: 5px;
}
.photo {
    border: 1px solid transparent;
    float: left;
    margin: 4px;
    overflow: hidden;
    padding: 4px;
    white-space: nowrap;

    /* CSS3 Box sizing property */
    -webkit-box-sizing: border-box;
       -moz-box-sizing: border-box;
         -o-box-sizing: border-box;
            box-sizing: border-box;

    /* CSS3 transition */
    -webkit-transition: border 0.2s ease 0s;    
       -moz-transition: border 0.2s ease 0s;
        -ms-transition: border 0.2s ease 0s;
         -o-transition: border 0.2s ease 0s;
            transition: border 0.2s ease 0s;
}
.photo:hover {
    border-color: #444;
}
.photo img {
    cursor: pointer;
    width: 200px;
}
.photo p, .photo i {
    display: block;
}
.photo p {
    font-weight: bold;
}

/* preview styles */
#photo_preview {
    background-color: rgba(0, 0, 0, 0.7);
    bottom: 0;
    color: #000000;
    display: none;
    left: 0;
    overflow: hidden;
    position: fixed;
    right: 0;
    top: 0;
    z-index: 10;
}
.photo_wrp {
    background-color: #FAFAFA;
    height: auto;
    margin: 100px auto 0;
    overflow: hidden;
    padding: 15px;
    text-align: center;
    vertical-align: middle;
    width: 1000px;
    border-radius: 5px;
}
.close {
    cursor: pointer;
    float: right;
}
.pleft {
    float: left;
    overflow: hidden;
    position: relative;
    width: 600px;
}
.pright {
    float: right;
    position: relative;
    width: 360px;
}
.preview_prev, .preview_next {
    cursor: pointer;
    margin-top: -64px;
    opacity: 0.5;
    position: absolute;
    top: 50%;

    -webkit-transition: opacity 0.2s ease 0s;
       -moz-transition: opacity 0.2s ease 0s;
        -ms-transition: opacity 0.2s ease 0s;
         -o-transition: opacity 0.2s ease 0s;
            transition: opacity 0.2s ease 0s;
}
.preview_prev:hover, .preview_next:hover {
    opacity: 1;
}
.preview_prev {
    left: 20px;
}
.preview_next {
    right: 40px;
}

/* comments styles */
#comments form {
    margin: 10px 0;
    text-align: left;
}
#comments table td.label {
    color: #000;
    font-size: 13px;
    padding-right: 3px;
    text-align: right;
    width: 105px;
}
#comments table label {
    color: #000;
    font-size: 16px;
    font-weight: normal;
    vertical-align: middle;
}
#comments table td.field input, #comments table td.field textarea {
    border: 1px solid #96A6C5;
    font-family: Verdana,Arial,sans-serif;
    font-size: 16px;
    margin-top: 2px;
    padding: 6px;
    width: 250px;
}
#comments_list {
    margin: 10px 0;
    text-align: left;
}
#comments_list .comment {
    border-top: 1px solid #000;
    padding: 10px 0;
}
#comments_list .comment:first-child {
    border-top-width:0px;
}
#comments_list .comment span {
    font-size: 11px;
}

Уверен, что этот материал будет полезен вам для собственных проектов. Удачи вам в вашей работе!

Статья подготовлена для вас коллективом сайта htmlhook.ru
Оригинал статьи: script-tutorials
Перевел: Виктор Клим
Правила использования материалов сайта!

автор 451
htmlhook.ru | Скрипты для веб-приложений