Статьи

Создание вашего стартапа с помощью PHP: настройки пользователя, изображения профиля и контактные данные

Конечный продукт
Что вы будете создавать

Это руководство является частью серии « Построение стартапа на PHP» для Tuts +. В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе мы будем публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, которые вы можете изучить. Мы также будем решать вопросы, связанные со стартапом, по мере их возникновения.

В этом руководстве мы собираемся построить ключевые области вокруг собственных данных пользователя для более широкого использования через приложение:

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

Весь код для Meeting Planner написан на Yii2 Framework для PHP, который имеет встроенную поддержку I18n. Если вы хотите узнать больше о Yii2, ознакомьтесь с нашей параллельной серией Программирование с Yii2 на Tuts +.

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

Целью контактных данных является предоставление пользователям возможности предоставлять номера телефонов и адреса видеоконференций для виртуальных собраний. Номер телефона также полезен до и после встречи.

Построить эту функцию относительно просто. Как и в предыдущих уроках, мы использовали генератор кода Yii, Gii, для построения модели для файлов UserContact и CRUD.

Мы также обновили панель навигации, добавив ссылку на функцию контакта с пользователем. В веб-интерфейсе / views / layouts / main.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
$menuItems[] = [
    ‘label’ => ‘Account’,
    ‘items’ => [
       [
          ‘label’ => Yii::t(‘frontend’,’Friends’),
          ‘url’ => [‘/friend’],
      ],
         [
            ‘label’ => Yii::t(‘frontend’,’Contact information’),
            ‘url’ => [‘/user-contact’],
        ],
         [
            ‘label’ => Yii::t(‘frontend’,’Settings’),
            ‘url’ => [‘/user-setting’],
        ],
         [
            ‘label’ => Yii::t(‘frontend’,’Logout’).’
            ‘url’ => [‘/site/logout’],
            ‘linkOptions’ => [‘data-method’ => ‘post’]
        ],
    ],
];
echo Nav::widget([
    ‘options’ => [‘class’ => ‘navbar-nav navbar-right’],
    ‘items’ => $menuItems,
]);

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

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

Добавьте контактную форму с типом выпадающего

Вот определения типов моделей и вспомогательные методы, позволяющие использовать дружественные раскрывающиеся списки для служб типов контактов:

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
class UserContact extends \yii\db\ActiveRecord
{
    const TYPE_OTHER = 0;
    const TYPE_PHONE = 10;
    const TYPE_SKYPE = 20;
    const TYPE_FACEBOOK = 30;
    const TYPE_GOOGLE = 40;
    const TYPE_MSN = 50;
    const TYPE_AIM = 60;
    const TYPE_YAHOO = 70;
    const TYPE_ICQ = 80;
    const TYPE_JABBER = 90;
    const TYPE_QQ = 100;
    const TYPE_GADU = 110;
     
    …
     
    public function getUserContactType($data) {
      $options = $this->getUserContactTypeOptions();
      return $options[$data];
    }
 
    public function getUserContactTypeOptions()
    {
      return array(
          self::TYPE_PHONE => ‘Phone’,
          self::TYPE_SKYPE => ‘Skype’,
          self::TYPE_OTHER => ‘Other’,
          self::TYPE_FACEBOOK => ‘Facebook Messenger’,
          self::TYPE_GOOGLE => ‘Google Talk’,
          self::TYPE_MSN => ‘MSN Messenger’,
          self::TYPE_AIM => ‘AIM’,
          self::TYPE_YAHOO => ‘Yahoo!
          self::TYPE_ICQ => ‘ICQ’,
          self::TYPE_JABBER => ‘Jabber’,
          self::TYPE_QQ => ‘QQ’,
          self::TYPE_GADU => ‘Gadu-Gadu’,
         );
     }

Вот выпадающий список реализован в виде:

1
2
3
4
5
6
7
8
9
<div class=»user-contact-form»>
 
   <?php $form = ActiveForm::begin();
 
   <?= $form->field($model, ‘contact_type’)
           ->dropDownList(
               $model->getUserContactTypeOptions(),
                   [‘prompt’=>Yii::t(‘frontend’,’What type of contact is this?’)]
               )->label(Yii::t(‘frontend’,’Type of Contact’)) ?>

Эти типы помощников являются общими конструкциями в Планировщике собраний.

Теперь перейдем к настройкам пользователя.

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

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

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

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

Нам также нужно создать форму, чтобы сделать настройку параметров простым процессом. Мы можем использовать макеты Bootstrap и расширения виджетов Yii2.

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

  • Фотография профиля пользователя (путь к файлу, который они загрузят)
  • Получение напоминаний за день до встречи
  • Получение напоминаний в дни, предшествующие встрече
  • Поделиться контактной информацией с участниками встречи
  • Заблокировать всю электронную почту из системы

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

1
./yii migrate/create create_user_setting_table

Вот миграция с полями для наших настроек — обратите внимание на связь с таблицей User:

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
<?php
 
use yii\db\Schema;
use yii\db\Migration;
 
class m150124_003721_create_user_setting_table extends Migration
{
  public function up()
  {
      $tableOptions = null;
      if ($this->db->driverName === ‘mysql’) {
          $tableOptions = ‘CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB’;
      }
 
      $this->createTable(‘{{%user_setting}}’, [
          ‘id’ => Schema::TYPE_PK,
          ‘user_id’ => Schema::TYPE_BIGINT.’
          ‘filename’ => Schema::TYPE_STRING.’
          ‘avatar’ => Schema::TYPE_STRING.’
          ‘reminder_eve’ => Schema::TYPE_SMALLINT.’
          ‘reminder_hours’ => Schema::TYPE_INTEGER.’
          ‘contact_share’ => Schema::TYPE_SMALLINT.’
          ‘no_email’ => Schema::TYPE_SMALLINT.’
          ‘created_at’ => Schema::TYPE_INTEGER .
          ‘updated_at’ => Schema::TYPE_INTEGER .
      ], $tableOptions);
      $this->addForeignKey(‘fk_user_setting_user_id’, ‘{{%user_setting}}’, ‘user_id’, ‘{{%user}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
  }
 
  public function down()
  {
      $this->dropForeignKey(‘fk_user_setting_user_id’, ‘{{%user_setting}}’);
      $this->dropTable(‘{{%user_setting}}’);
  }
}

Запустите миграцию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
./yii migrate/up
Yii Migration Tool (based on Yii v2.0.2)
 
Total 1 new migration to be applied:
    m150124_003721_create_user_setting_table
 
Apply the above migration?
*** applying m150124_003721_create_user_setting_table
    > create table {{%user_setting}} … done (time: 0.017s)
    > add foreign key fk_user_setting_user_id: {{%user_setting}} (user_id) references {{%user}} (id) … done (time: 0.009s)
*** applied m150124_003721_create_user_setting_table (time: 0.031s)
 
 
Migrated up successfully.

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

Вот настройки для Генератора моделей:

Yii Code Generator Gii с пользовательской настройкой модели

Вот настройки для контроллера CRUD:

Yii Gii Code Generator CRUD для пользовательских настроек

Когда пользователь нажимает на «Настройки» на панели навигации, мы действительно хотим отобразить страницу настроек обновления. Давайте создадим несколько помощников для проверки записи настроек пользователя и создания ее, если она не существует.

Вот перенаправление индекса на действие обновления:

01
02
03
04
05
06
07
08
09
10
/**
    * Default path — redirect to update
    * @return mixed
    */
   public function actionIndex()
   {
     // returns record id not user_id
     $id = UserSetting::initialize(Yii::$app->user->getId());
     return $this->redirect([‘update’, ‘id’ => $id]);
   }

Метод Initialize создает запись для активного пользователя и устанавливает все настройки по умолчанию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public static function initialize($user_id) {
     $us = UserSetting::find()->where([‘user_id’=>$user_id])->one();
     if (is_null($us)) {
       $us=new UserSetting;
       $us->user_id = $user_id;
       $us->filename=»;
       $us->avatar=»;
       $us->reminder_eve = self::SETTING_YES;
       $us->no_email = self::SETTING_NO;
       $us->contact_share = self::SETTING_YES;
       $us->reminder_hours = 48;
       $us->save();
     }
     return $us->id;
   }

Мы должны заменить некоторые из автоматически сгенерированного кода формы, чтобы включить флажки и другой раскрывающийся список (как мы сделали с типом UserContact выше). Мне нравится, что функциональность флажков Yii включает в себя возможность задавать установленные и неустановленные значения. Как правило, флажки веб-формы возвращают пустой элемент (существует) для true или не возвращают (не существует) для false.

01
02
03
04
05
06
07
08
09
10
11
<?= $form->field($model, ‘reminder_eve’)->checkBox([‘label’ => Yii::t(‘frontend’,’Send final reminder the day before a meeting’), ‘uncheck’ => $model::SETTING_NO, ‘checked’ => $model::SETTING_YES]);
 
        <?= $form->field($model, ‘reminder_hours’)
                ->dropDownList(
                    $model->getEarlyReminderOptions(),
                        [‘prompt’=>Yii::t(‘frontend’,’When would you like your early reminder?’)]
                   )->label(Yii::t(‘frontend’,’Early reminders’)) ?>
 
           <?= $form->field($model, ‘contact_share’)->checkbox([‘label’ =>Yii::t(‘frontend’,’Share my contact information with meeting participants’),’uncheck’ => $model::SETTING_NO, ‘checked’ => $model::SETTING_YES]);
 
           <?= $form->field($model, ‘no_email’)->checkbox([‘label’ =>Yii::t(‘frontend’,’Turn off all email’),’uncheck’ => $model::SETTING_NO, ‘checked’ => $model::SETTING_YES]);

Вот конечный результат:

Планировщик собраний Обновите свои настройки

Давайте продолжим и расширим UserSetting для поддержки фотографий профиля.

Чтобы показывать фотографии пользователей на страницах собраний, мы должны поддерживать пользователей, загружающих фотографии (в большинстве случаев). В качестве альтернативы, по крайней мере, по умолчанию, мы можем использовать сервис Gravatar, который связывает фотографию профиля с адресом электронной почты пользователя. Загрузка и сохранение файлов на сервере — это всегда сложная и деликатная задача веб-разработки, которую мы вскоре рассмотрим.

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

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
<!— Nav tabs —>
<ul class=»nav nav-tabs» role=»tablist»>
  <li class=»active»><a href=»#general» role=»tab» data-toggle=»tab»>Settings</a></li>
  <li><a href=»#photo» role=»tab» data-toggle=»tab»>Upload Photo</a></li>
</ul>
<!— Tab panes —>
<div class=»tab-content»>
  <div class=»tab-pane active vertical-pad» id=»general»>
      <?= $form->field($model, ‘reminder_eve’)->checkBox([‘label’ => Yii::t(‘frontend’,’Send final reminder the day before a meeting’), ‘uncheck’ => $model::SETTING_NO, ‘checked’ => $model::SETTING_YES]);
 
      <?= $form->field($model, ‘reminder_hours’)
              ->dropDownList(
                  $model->getEarlyReminderOptions(),
                      [‘prompt’=>Yii::t(‘frontend’,’When would you like your early reminder?’)]
                )->label(Yii::t(‘frontend’,’Early reminders’)) ?>
 
         <?= $form->field($model, ‘contact_share’)->checkbox([‘label’ =>Yii::t(‘frontend’,’Share my contact information with meeting participants’),’uncheck’ => $model::SETTING_NO, ‘checked’ => $model::SETTING_YES]);
 
         <?= $form->field($model, ‘no_email’)->checkbox([‘label’ =>Yii::t(‘frontend’,’Turn off all email’),’uncheck’ => $model::SETTING_NO, ‘checked’ => $model::SETTING_YES]);
       </div>
  <div class=»tab-pane vertical-pad» id=»photo»>
 
    <?= $form->field($model, ‘image’)->widget(FileInput::classname(), [
        ‘options’ => [‘accept’ => ‘image/*’],
         ‘pluginOptions’=>[‘allowedFileExtensions’=>[‘jpg’,’gif’,’png’]],
    ]);
  </div> <!— end of upload photo tab —>
</div>

Это выглядит так:

Настройки пользователя с вкладками Bootstrap

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

Есть четыре расширения Yii2, которые я хочу включить для поддержки изображения профиля:

  1. Gravatar Карстена Брандта для отображения граватары
  2. Kartik Visweswaran File Input для поддержки загрузки файлов изображений
  3. Yii Представьте себе, чтобы масштабировать изображения до разных размеров
  4. 2Amigos Resource Manager для дальнейшей поддержки хранилища Amazon S3

Visweswaran и 2Amigos выпустили несколько расширений Yii2, которые очень полезны и, как правило, также предоставляют надежную документацию.

Итак, добавьте эти расширения в ваш файл composer.json:

1
2
3
4
5
«cebe/yii2-gravatar»: «*»,
       «kartik-v/yii2-widget-fileinput»: «*»,
       «yiisoft/yii2-imagine»: «*»,
       «2amigos/yii2-switch-widget»: «0.1.*»,
       «2amigos/yii2-resource-manager-component»: «0.1.*»

И обновить композитора:

1
composer update

Если изображение профиля не настроено, мы отобразим граватар пользователя. Сервис Gravatar ссылается на адрес электронной почты зарегистрированного пользователя. Вот код для отображения Gravatar:

1
2
3
4
5
6
7
8
echo \cebe\gravatar\Gravatar::widget([
    ’email’ => common\models\User::find()->where([‘id’=>Yii::$app->user->getId()])->one()->email,
    ‘options’ => [
        ‘class’=>’profile-image’,
        ‘alt’ => common\models\User::find()->where([‘id’=>Yii::$app->user->getId()])->one()->username,
    ],
    ‘size’ => 128,
]);

Это будет выглядеть примерно так. Если пользователь настроил Gravatar, вы увидите его выбранное изображение.

Отображение Граватара с Yii2

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

Вот код нашей формы обновления, которая отображает виджет на панели «Загрузить фото»:

1
2
3
4
5
6
<div class=»tab-pane vertical-pad» id=»photo»>
  <?= $form->field($model, ‘image’)->widget(FileInput::classname(), [
      ‘options’ => [‘accept’ => ‘image/*’],
       ‘pluginOptions’=>[‘allowedFileExtensions’=>[‘jpg’,’gif’,’png’]],
  ]);
</div> <!— end of upload photo tab —>

Верхняя часть формы включает в себя enctype для данных формы, состоящей из нескольких частей:

1
2
3
4
5
<div class=»user-setting-form»>
   <?php
   $form = ActiveForm::begin([
       ‘options’=>[‘enctype’=>’multipart/form-data’]]);
        ?>

Форма выглядит так:

Расширение файла ввода Yii2 - Загрузить изображение профиля

Мы также добавили правила в модель UserSetting, чтобы ограничить загрузку в jpg, gif и png и ограничить размер загрузки до 100 КБ.

01
02
03
04
05
06
07
08
09
10
11
12
public function rules()
   {
       return [
           [[‘user_id’, ], ‘required’],
           [[‘user_id’, ], ‘unique’],
           [[‘image’], ‘safe’],
           [[‘image’], ‘file’, ‘extensions’=>’jpg, gif, png’],
           [[‘image’], ‘file’, ‘maxSize’=>’100000’],
            [[‘filename’, ‘avatar’], ‘string’, ‘max’ => 255],
           [[‘user_id’, ‘reminder_eve’, ‘reminder_hours’, ‘contact_share’, ‘no_email’, ‘created_at’, ‘updated_at’], ‘integer’],
       ];
   }

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
if ($model->load(Yii::$app->request->post())) {
          $image = UploadedFile::getInstance($model, ‘image’);
          if (!is_null($image)) {
            // save with image
             // store the source file name
            $model->filename = $image->name;
            $ext = end((explode(«.», $image->name)));
            // generate a unique file name to prevent duplicate filenames
            $model->avatar = Yii::$app->security->generateRandomString().».{$ext}»;
            // the path to save file, you can set an uploadPath
            // in Yii::$app->params (as used in example below)
            Yii::$app->params[‘uploadPath’] = Yii::$app->basePath .
            $path = Yii::$app->params[‘uploadPath’] .
            $model->user_id = Yii::$app->user->getId();
            if($model->update()){
              $image->saveAs($path);

Изображения должны храниться там, где они могут быть просмотрены веб-сервером. Я создал каталог загрузок / аватаров в дереве / frontend / web.

На самом деле мы хотим хранить изображение в трех размерах: полный размер, один маленький квадрат для отображения на панели навигации или в виде списка, и один квадрат среднего размера для страниц собрания.

Мы будем использовать расширение Yii2 Imagine для масштабирования изображений; он расширяет библиотеку Imagine для работы с изображениями для PHP .

Вот код, который масштабирует и сохраняет дополнительные размеры изображения:

1
2
3
4
5
$image->saveAs($path);
Image::thumbnail(Yii::$app->params[‘uploadPath’].$model->avatar, 120, 120)
    ->save(Yii::$app->params[‘uploadPath’].’sqr_’.$model->avatar, [‘quality’ => 50]);
Image::thumbnail(Yii::$app->params[‘uploadPath’].$model->avatar, 30, 30)
        ->save(Yii::$app->params[‘uploadPath’].’sm_’.$model->avatar, [‘quality’ => 50]);

Вот готовая форма, отображающая квадратное изображение на странице профиля пользователя:

Планировщик собраний Обновите свои настройки с помощью вкладок и изображения профиля

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

1
2
3
4
5
6
7
8
$image = UploadedFile::getInstance($model, ‘image’);
          if (!is_null($image)) {
            // path to existing image for post-delete
            $image_delete = $model->avatar;
             
            …
             
            $model->deleteImage(Yii::$app->params[‘uploadPath’],$image_delete);

Вот метод deleteImage в UserSetting:

01
02
03
04
05
06
07
08
09
10
11
12
13
public function deleteImage($path,$filename) {
          $file =array();
          $file[] = $path.$filename;
          $file[] = $path.’sqr_’.$filename;
          $file[] = $path.’sm_’.$filename;
          foreach ($file as $f) {
            // check if file exists on server
            if (!empty($f) && file_exists($f)) {
              // delete file
              unlink($f);
            }
          }
      }

Хранение загруженных пользователем изображений на нашем веб-сервере может создать сложности. Это означает, что в дополнение к резервному копированию MySQL нам также необходимо всегда выполнять резервное копирование части файловой системы. Это может усложнить операции восстановления и миграции сервера. Он также загружает наш стек LAMP всякий раз, когда загружаются изображения.

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

Я надеюсь, что вы изучили некоторые прикладные аспекты Yii, ActiveRecord, Bootstrap, форм, загрузки файлов и работы с изображениями. Мне понравилось работать с некоторыми новыми виджетами JQuery, доступными как расширения Yii. Следите за будущими уроками в нашей серии «Построение стартапа с помощью PHP» — впереди много интересных функций. Фактически, мы приближаемся к возможности запланировать нашу первую встречу!

Пожалуйста, не стесняйтесь добавлять свои вопросы и комментарии ниже; Я обычно участвую в обсуждениях. Вы также можете связаться со мной в Twitter @reifman или написать мне напрямую.