Статьи

Создание стены с тегами фотографий с помощью Twilio Picture Messaging и PHP

Недавно анонсированная Twilio программа Picture Messaging открыла широкие возможности для работы с текстовыми сообщениями, теперь мы можем прикреплять фотографии к нашим текстовым сообщениям и использовать их различными способами.

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

Это может быть удобно для событий или вечеринок, или для всего, что вы хотите связать фотографии и теги.

Для обработки наших фотографий мы будем делать несколько разных вещей; Мы собираемся хранить их локально, а затем изменять их размер, чтобы они лучше отображались на наших экранах.

Мы собираемся использовать Микрофрейм Jolt для PHP, а также Idiorm и Paris для обработки MySql.


Хорошо, сначала давайте настроим нашу базу данных:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
CREATE TABLE `tag`(
 
`id` bigint(20) NOT NULL AUTO_INCREMENT,
 
`name` varchar(255) NOT NULL DEFAULT »,
 
`slug` varchar(255) NOT NULL DEFAULT »,
 
PRIMARY KEY (`id`),
 
KEY `name` (`name`),
 
KEY `slug` (`slug`)
 
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
 
CREATE TABLE `photo`(
 
`id` bigint(20) NOT NULL AUTO_INCREMENT,
 
`tag_id` bigint(20) NOT NULL DEFAULT ‘0’,
 
`file` varchar(255) NOT NULL DEFAULT »,
 
`from` varchar(255) NOT NULL DEFAULT »,
 
`country` varchar(255) NOT NULL DEFAULT »,
 
`datetime` timestamp DEFAULT CURRENT_TIMESTAMP,
 
PRIMARY KEY (`id`),
 
KEY `tag_id` (`tag_id`),
 
KEY `file` (`file`)
 
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

Мы настраиваем две таблицы: одну для хранения тега, другую для фотографий и идентификатора для тега, с которым они связаны.

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

Нам также понадобится загрузить инфраструктуру Jolt, библиотеку PHP Twilio, а также Idiorm и Paris.

Первое, что вы хотите сделать, это получить эти пакеты с соответствующих веб-сайтов:

Теперь, когда у вас есть все пакеты, загруженные на ваш компьютер, пришло время настроить структуру каталогов. Мы будем помещать файлы в корневую папку нашего сайта.

Мы помещаем файлы, связанные с веб-службами, в папку « Services », так как это помогает нам отслеживать, где что находится.

Хорошо, давайте config.ini наш файл config.ini , откройте файл config.ini в вашем редакторе и измените следующие параметры:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
;site settings
 
site.name = my site
 
site.url = http://mysiteurl.com
 
;
 
views.root = views
 
views.layout = layout
 
;
 
cookies.secret = IeNj0yt0sQu33zeflUFfym0nk1e
 
cookies.flash = _F
 
;
 
db.enabled = true
 
db.host = MY DATABASE HOST
 
db.name = MY DATABASE NAME
 
db.user = MY DATABASE USER
 
db.pass = MY DATABASE PASSWORD
 
;
 
twilio.accountsid = MY TWILIO ACCOUNT SID
 
twilio.authtoken = MY TWILIO AUTH TOKEN
 
twilio.from = MY TWILIO FROM NUMBER

Вы можете увидеть, что вам нужно будет заполнить здесь, название вашего сайта и URL, информацию о вашей базе данных и информацию о Twilio.


Для начала давайте настроим наши модели. Мы создадим файл в system папке с именем models.php :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
 
class Tag extends Model{
 
    public function photos(){
 
        return $this->has_many(‘Photo’);
 
    }
 
}
 
class Photo extends Model {
 
    public function tag(){
 
        return $this->belongs_to(‘Tag’);
 
    }
 
}
 
?>

Это довольно простой макет модели, но одна приятная вещь в том, что мы используем Париж, чтобы установить отношения с таблицей tag . Фактически, поскольку мы ранее создали нашу базу данных, чтобы в таблице photo поле tag_id , эта модель знает, как связать все фотографии с tag_id , где tag_id — это имя таблицы и поле первичного ключа в таблице tag .

То же самое верно для класса Photo , где мы установили, что он принадлежит тегу, как указано в функции tag() .

Это удобно для построения быстрой модели системы без больших накладных расходов.

Мы также хотим создать наш файл functions.php , который мы также будем хранить в system папке:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php
 
function slugify( $string ){
 
    $string = strtolower( trim($string) );
 
    $slug=preg_replace(‘/[^A-Za-z0-9-]+/’, ‘-‘, $string);
 
    return $slug;
 
}
 
function cropResize($img,$out=»,$dSize=170){
 
    $x = @getimagesize($img);
 
    $sw = $x[0];
 
    $sh = $x[1];
 
    $yOff = 0;
 
    $xOff = 0;
 
    if($sw < $sh) {
 
        $scale = $dSize / $sw;
 
        $yOff = $sh/2 — $dSize/$scale/2;
 
    } else {
 
        $scale = $dSize / $sh;
 
        $xOff = $sw/2 — $dSize/$scale/2;
 
}
 
$im = @ImageCreateFromJPEG ($img) or // Read JPEG Image
 
$im = @ImageCreateFromPNG ($img) or // or PNG Image
 
$im = @ImageCreateFromGIF ($img) or // or GIF Image
 
$im = false;
 
if (!$im) {
 
    readfile ($img);
 
} else {
 
    $thumb = @ImageCreateTrueColor ($dSize,$dSize);
 
    imagecopyresampled($thumb, $im,
 
    0, 0,
 
    $xOff,$yOff,
 
    $dSize, $dSize,
 
    $dSize / $scale ,$dSize / $scale);
 
}
 
if( $out == » ){
 
    header(‘content-type:image/jpeg’);
 
    imagejpeg($thumb);
 
}else{
 
    imagejpeg($thumb, $out);
 
}
 
}

functions.php будет содержать две основные функции: одна функция, slugify() , преобразует имена тегов в cropResize() , а cropResize() возьмет изображение, которое мы передаем ему, и сохранит его в новых измерениях.

Мы будем использовать эти функции довольно скоро.

Большая часть нашего кода будет храниться в index.php , поэтому давайте настроим для него все необходимое:

01
02
03
04
05
06
07
08
09
10
11
12
13
<?php
 
include(«system/jolt.php»);
 
require ‘system/idiorm.php’;
 
require ‘system/paris.php’;
 
require ‘system/models.php’;
 
require ‘Services/Twilio.php’;
 
require ‘system/functions.php’;

Хорошо, мы включили наши файлы, и ничего не произошло. Теперь давайте запустим Jolt:

1
2
3
$app = new Jolt();
 
$app->option(‘source’, ‘config.ini’);

Приведенный выше код просто устанавливает Jolt и говорит ему прочитать файл config.ini и установить наши параметры конфигурации, теперь давайте подключимся к нашей базе данных:

1
2
3
4
5
6
7
8
9
if( $app->option(‘db.enabled’) != false ){
 
    ORM::configure(‘mysql:host=’.$app->option(‘db.host’).’;dbname=’.$app->option(‘db.name’));
 
    ORM::configure(‘username’, $app->option(‘db.user’) );
 
    ORM::configure(‘password’, $app->option(‘db.pass’) );
 
}

Наша последняя часть начальной загрузки, мы хотим настроить наш клиент Twilio:

1
2
3
4
5
$client = new Services_Twilio($app->option(‘twilio.accountsid’), $app->option(‘twilio.authtoken’) );
 
$fromNumber = $app->option(‘twilio.from’);
 
$app->store(‘client’,$client);

Это наш раздел начальной загрузки, пока все, что мы сделали, это включили наши файлы, настроили наше приложение Jolt, подключились к нашей базе данных и инициализировали наш клиент Twilio.

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


Теперь мы должны настроить наши маршруты и сообщить нашему приложению, что делать, основываясь на определенных правилах. Эти правила будут либо get либо post .

Нашими начальными правилами будут домашняя страница, страница тега и слушатель:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
$app->get(‘/’,function(){
 
$app = Jolt::getInstance();
 
});
 
$app->get(‘/tag/:tag’,function($tag){
 
$app = Jolt::getInstance();
 
});
 
$app->post(‘/listener’,function($tag){
 
$app = Jolt::getInstance();
 
});
 
$app->listen();

Мы только что настроили начальные действия для нашей домашней страницы, которые представлены символом '/' , нашей страницей тегов и нашим слушателем.

Вы заметите, что слушатель является post а не получателем, потому что это обработчик Twilio при получении новых сообщений.

Наконец, вы увидите $app->listen(); вызов метода. Это самый важный вызов метода, который у нас есть, так как он сообщает приложению о запуске.


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

Замените исходный маршрут домашней страницы этим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$app->get(‘/’, function(){
 
$app = Jolt::getInstance();
 
$tags = Model::factory(‘Tag’)->count();
 
if( isset($tags) ){
 
    $images = Model::factory(‘Photo’)->count();
 
    $tagList = Model::factory(‘Tag’)->find_many();
 
}else{
 
    $tags = 0;
 
    $images = 0;
 
    $tagList = array();
 
}
 
$app->render( ‘home’,array(
 
    ‘tags’=>$tags,
 
    ‘tagList’ => $tagList,
 
    ‘fromNumber’ => $app->option(‘twilio.from’),
 
    ‘images’=>$images
 
    ));
 
});

Вы также заметите, что мы просим его визуализировать что-то, называемое home , в папке views есть файл home.php , откройте его и отредактируйте следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<p >Text <span><?php echo $fromNumber ?>
 
<div>
 
    <div>
 
        <p>Number of Tags: <?php echo $tags;
 
        <p>Number of Images: <?php echo $images;
 
    </div>
 
</div>
 
<hr />
 
<h3>Tags</h3>
 
<ul>
 
<?php  foreach($tagList as $tag){ ?>
 
    <li>
 
    <a href=»<?php echo $uri?>/tag/<?php echo $tag->slug?>»><?php echo $tag->name?></a>
 
    </li>
 
<?php  }  ?>
 
</ul>

Этот файл будет принимать переменные, которые мы передаем из функции $app->render() и использовать их здесь.

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

Фактический макет страницы контролируется файлом с именем layout.php . Давайте продолжим и обновим этот файл сейчас:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<html>
 
    <head>
 
        <title><?=$pageTitle?></title>
 
        <meta name=»viewport» content=»width=device-width, initial-scale=1.0″>
 
        <link href=»//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css» rel=»stylesheet»>
 
        <link href=»<?=$uri?>/style.css» rel=»stylesheet»>
 
        <script src=»//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js»></script>
 
        <script src=»//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js»></script>
 
    </head>
 
    <body>
 
        <div>
 
            <div>
 
                <ul>
 
                    <li><a href=»<?=$uri?>»>Home</a></li>
 
                </ul>
 
                <h3>Photo Wall</h3>
 
            </div>
 
        <hr />
 
        <section>
 
            <?=$pageContent?>
 
        </section>
 
        </div>
 
    </body>
 
</html>

Это довольно простой HTML, но он охватывает то, что нам нужно. Все выходные данные отправляются в переменную $pageContent в layout.php .


Хорошо, теперь давайте обработаем фактическую загрузку фотографий из Twilio.

Войдите в свою учетную запись Twilio и укажите номер телефона по http://MYSITEURL/listener для SMS-сообщений, где MYSITEURL — это адрес, на который вы загрузили свое приложение.

Мы собираемся заменить наш маршрут слушателя следующим:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
$app->post(‘/listener’, function(){
 
$app = Jolt::getInstance();
 
if ( isset($_POST[‘NumMedia’]) && $_POST[‘NumMedia’] > 0 ){
 
    // let’s find out what tag this is for.. or create a new one..
 
    $thetag = slugify( $_POST[‘Body’] );
 
    $tag = Model::factory(‘Tag’)->where_equal( ‘slug’, $thetag )->find_one();
 
    if( isset($tag->id) && !empty($tag->id) ){
 
        $tag_id = $tag->id;
 
    }else{
 
        // no tag already exists…
 
        $tag          = Model::factory(‘Tag’)->create();
 
        $tag->name       = $_POST[‘Body’];
 
        $tag->slug       = slugify( $_POST[‘Body’] );
 
        $tag->save();
 
        $tag_id = $tag->id();
 
    }
 
    for ($i = 0; $i < $_POST[‘NumMedia’]; $i++){
 
    if (strripos($_POST[‘MediaContentType’.$i], ‘image’) === False){
 
        continue;
 
    }
 
    $file = sha1($_POST[‘MediaUrl’.$i]).’.jpg’;
 
    file_put_contents(‘images/original/’.$file, file_get_contents($_POST[‘MediaUrl’.$i]));
 
    chmod (‘images/original/’.$file, 01777);
 
 
    // Edit image
 
    $in = ‘images/original/’.$file;
 
    $out = ‘images/processed/’.$file;
 
    cropResize($in,$out,250);
 
    chmod (‘images/processed/’.$file, 01777);
 
 
    // Remove Original Image
 
    unlink(‘images/original/’.$file);
 
    $photo       =  Model::factory(‘Photo’)->create();
 
    $photo->tag_id   =  $tag_id;
 
    $photo->file    =  $file;
 
    $photo->from    =  $_POST[‘From’];
 
    $photo->country   =  $_POST[‘FromCountry’];
 
    $photo->save();
 
    }
 
    $message = $app->store(‘client’)->account->messages->sendMessage(
 
    $app->option(‘twilio.from’), // From a valid Twilio number
 
    $_POST[‘From’], // Text this number
 
    «Image(s) Added to <«.strtolower(trim($_POST[‘Body’])).»> Photo Wall Link: «.$app->option(‘site.url’).»/tag/».strtolower(trim($_POST[‘Body’]))
 
    );
 
    return true;
 
}else{
 
    if ( isset($_POST[‘From’]) ){
 
    $message = $app->store(‘client’)->account->messages->sendMessage(
 
    $app->option(‘twilio.from’), // From a valid Twilio number
 
    $_POST[‘From’], // Text this number
 
    «MMS error. Please try sending your image again.»
 
    );
 
}
 
header(‘HTTP/1.1 400 Bad Request’, true, 400);
 
return false;
 
}
 
});

Нет представления, связанного с этим действием. Теперь давайте рассмотрим, что он делает.

Это вызывается только во время post , следовательно, $app->post() .

Когда он активируется кем-то, отправляющим сообщение, мы проверяем, есть ли прикрепленные изображения, и если они есть, то мы их циклически повторяем и сохраняем в базе данных.

Сначала мы проверяем, есть ли какие-либо теги, уже сохраненные в нашей базе данных, которые соответствуют тегу, который мы прикрепили к нашему изображению, и, если есть, то мы берем id из базы данных, в противном случае мы сохраняем новую запись, содержащую этот тег. Информация.

Далее мы перебираем загруженные файлы и проверяем, являются ли они изображениями. Каждое изображение загружается локально и сохраняется в папке images/original . Затем мы изменяем размеры и обрезаем изображения, чтобы получить более управляемый размер, и сохраняем новые файлы в папке images/processed processing.

Наконец, мы сохраняем изображения в базе данных вместе с некоторыми метаданными о самом вызове и отправляем текстовое сообщение отправителю, чтобы он или она проверили страницу тега.

Если изображения не были прикреплены, то мы отправляем им сообщение о том, что произошла ошибка.


Теперь мы создали домашнюю страницу и настроили слушателя. Осталось настроить саму фотостену.

Это будет происходить внутри $app->get('/tag/:tag') .

Замените исходный заполнитель следующим кодом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// preload photos whenever a matching route has :tag in it
 
$app->filter(‘tag_slug’, function ($tag_slug){
 
$app = Jolt::getInstance();
 
$tag = Model::factory(‘Tag’)->where_equal(‘slug’,$tag_slug)->find_one();
 
$photos = $tag->photos()->find_many();
 
$app->store(‘tag’, $tag);
 
$app->store(‘photos’, $photos);
 
});
 
$app->get(‘/tag/:tag_slug’, function($tag_slug){
 
$app = Jolt::getInstance();
 
$tag = $app->store(‘tag’);
 
$photos = $app->store(‘photos’);
 
$app->render( ‘gallery’, array(
 
«pageTitle»=>»viewing Photos for {$tag->name}»,
 
«tag»=>$tag,
 
«photos»=>$photos
 
));
 
});

Обратите внимание на $app->filter() Это удобный метод, который мы можем настроить, который будет захватывать тег и его фотографии каждый раз, когда $tag_slug переменная $tag_slug , это позволяет нам сэкономить на дополнительных запросах.

Теперь нам нужно настроить страницу gallery.php внутри views :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<div>
 
    <h1>#<?php echo $tag->name;
 
</div>
 
<hr />
 
<div>
 
    <div>
 
        <div id=»container»>
 
            <?php  foreach($photos as $photo){ ?>
 
            <?php if (file_exists(‘images/processed/’.$photo->file)){ ?>
 
        <div>
 
    <div>
 
    <a href=»<?php echo $uri?>/images/processed/<?php echo $photo->file ?>» title=»<?php echo $photo->datetime ?>» >
    <img src=»<?php echo $uri?>/images/processed/<?php echo $photo->file ?>» /></a>
 
    <p><?php echo $photo->datetime?></p>
 
</div>
 
</div>
 
<?php } ?>
 
<?php } ?>
 
</div>
 
</div>
 
</div>
 
<script src=»//cdnjs.cloudflare.com/ajax/libs/masonry/3.1.1/masonry.pkgd.min.js»></script>
 
<script type=»text/javascript»>
 
var container = document.querySelector(‘#container’);
 
var msnry = new Masonry( container, {
 
itemSelector: ‘.image’
 
});
 
</script>

Это отобразит галерею и использует кладку jQuery для плавного размещения всех изображений.


Итак, это завершает наше приложение. Теперь вы построили небольшую удобную фотостену, которую можно использовать для показа фотографий с событий. Обязательно ознакомьтесь с приведенными выше ссылками, чтобы узнать больше о библиотеках и платформах, используемых в этой статье. Спасибо за прочтение.