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

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

Демо

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

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

Шаг 1. SQL

Для фотогалереи нужны две таблицы: первая таблица содержит записи о изображениях. И несколько полей: название, имя файла, описание, время добавления и количество комментарии. Другая таблица сохраняет комментарии. Выполним следующий запрос 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;


Шаг 2. PHP

Теперь, создайте пустой файл index.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=
http://pixelcom.crimea.ua/"images/thumb_'.$aItemInfo['filename'].'" id="'.$aItemInfo['id'].'" /><p>'.$aItemInfo['title'].' item</p><i>'.$aItemInfo['description'].'</i></div>';
}

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

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

    <!-- Ссылка скрипты -->
<script src=
http://pixelcom.crimea.ua/"https://www.google.com/jsapi"></script>
<script>
        google.load("jquery", "1.7.1");
    </script>
<script src=
http://pixelcom.crimea.ua/"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=
http://pixelcom.crimea.ua/"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. Кроме того, когда мы публикуем комментарии, мы ожидаем этот запрос (принять новый комментарий) в классе комментариев. Теперь, давайте посмотрим следующий важный файл PHP:

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=
http://pixelcom.crimea.ua/"images/prev.png" alt="prev" /></div>' : '';
    $sNextBtn = ($iNext) ? '<div class="preview_next" onclick="getPhotoPreviewAjx(\''.$iNext.'\')"><img src=
http://pixelcom.crimea.ua/"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=
http://pixelcom.crimea.ua/"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. Вы можете настроить параметры базы данных в базу данных класса. Оба класса в нашем пакете.

Шаг 3. 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 инструкции в наш сценарий (я надеюсь, что вы не забыли, что мы связаны с библиотекой jQuery в заголовке через Google сервис).

Шаг 4. 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;

    -moz-border-radius: 5px;
    -ms-border-radius: 5px;
    -o-border-radius: 5px;
    -webkit-border-radius: 5px;
    border-radius: 5px;
}
.photo {
    border: 1px solid transparent;
    float: left;
    margin: 4px;
    overflow: hidden;
    padding: 4px;
    white-space: nowrap;

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

    /* CSS3 transition */
    -moz-transition: border 0.2s ease 0s;
    -ms-transition: border 0.2s ease 0s;
    -o-transition: border 0.2s ease 0s;
    -webkit-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;

    -moz-border-radius: 5px;
    -ms-border-radius: 5px;
    -o-border-radius: 5px;
    -webkit-border-radius: 5px;
    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%;

    -moz-transition: opacity 0.2s ease 0s;
    -ms-transition: opacity 0.2s ease 0s;
    -o-transition: opacity 0.2s ease 0s;
    -webkit-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;
}


Заключение

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

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

13 комментариев на тему “Фотогалерея с комментариями”

  1. комментарии

    Подскажите пожалуйста. Здесь не написано о создании БД. Создаю БД «test», импортирую в неё sql.sql и выдает следующее: 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;

    Ответ MySQL: Документация
    #1046 — No database selected

    Понимаю, что вопрос покажется глупым, но я в первый раз сталкиваюсь с созданием сайта и отсюда низкий уровень вопроса.

  2. комментарии

    Приношу извинения, поторопился с вопросом. Всё заработало, огромное Вам спасибо.

  3. комментарии

    Только что проверил, всё работает.

    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;# MySQL вернула пустой результат (т.е. ноль строк).
    # MySQL вернула пустой результат (т.е. ноль строк).

    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);# Затронуто 10 строк.
    # Затронуто 10 строк.

    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;# MySQL вернула пустой результат (т.е. ноль строк).
    # MySQL вернула пустой результат (т.е. ноль строк).
    # MySQL вернула пустой результат (т.е. ноль строк).

  4. комментарии

    Тогда, если не сложно, подскажите, как исправить описание фотографии
    При вводе русского названия и описания выходит » фото 1 item
    описание » и где убрать слово «item» , единственно оставшееся латиницей и не присутствующее в полях таблицы при исправлении. Буду крайне признателен.

  5. комментарии

    Вобще супер! Вот ещё бы реализовать разбиение по альбомам…
    А Так Огромное спаибо! Очень помогло!

  6. комментарии

    Задумка хорошая.
    Вот только есть замечание по комментариям. Как-то они уж не совсем корректно добавляются и отображаются. При открытии фотографии — показывается только один комментарий, после добавления всплывают все комментарии, и от других фото в том числе.

  7. комментарии

    Хорошая галерея ток как сделать что бы коменты нормально заливались? а то виден только первый а потом все остальные((

  8. комментарии

    Побробуй для #comments_list добавить display: block;

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML -теги и атрибуты: <a href= http://pixelcom.crimea.ua/"" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>