• Изменено

У меня установлен RainLab.User
В админке пользователю можно добавить аватар и потом аватар можно вывести на фронте.

А как реализовать, чтобы пользователи могли загрузить себе аватар?

  1. Создал страницу Редактирования профиля
  2. Добавил в неё компонент из RainLab.User "Аккаунт - управление формой"
  3. Установил флажок Скрытая страница
  4. Для удобства псевдоним указал editProfile (в окошке настроек, где обычно указывается slug, кол-во для пагинации и т.д.)

Тут же (CMS-Страницы-Редактирование профиля) в Разметке вместо обычного {% component .... %} вставил код компонента, чтобы не писать в шаблоне переопределение:

<img class="img-thumbnail" src="{{ user.avatar.thumb(100,100,{mode:'crop'})}}">
<input type="hidden" name="_handler" value="onUpdate">
<input type="file" accept="image/*" name="avatar" id="imageUpload">
<img class="img-thumbnail" id="imageUploadPreview" width="100px" height="100px">

Это почти оригинальный код компонента. Я в него ещё 4 строчки вставил (они идут подряд сразу после form_ajax) - вывод аватара и инпут для загрузки изображений.

Как мне теперь заставить это работать чтобы редактирование аватаров заработало?

  • Linkonoid ответили на это сообщение.
    15 дней спустя

    Koresh Решал подобную задачу, если ещё актуально и не нашли решения, то пример ниже. Аватары сами по себе в базу не попадают (точнее их привязка к пользователю), нужно писать код со стороны бакенда для этого.
    Загрузка аватара через модальное окно (настройте под себя, можно и не из модального окна, а из общей формы с данными пользователя и повесить загрузку на событие выбора изображения onchange + вызов оттуда onAvatarUpdate через Ajax-javascript API):

    <div class="modal-avatar">
        <h4>Загрузить аватарку</h4>
        <form data-request="onAvatarUpdate" data-request-files data-attach-loading data-request-success="$('#modal').modal('toggle'); return false;">
            <div class="form-group">
                <input type="hidden" name="MAX_FILE_SIZE" value="2097152" /> 
                <input type="file" name="avatar" id="file" onchange="$('label[for=\'file\']').html('<span class=\'glyphicon glyphicon-download-alt\'></span>'+this.files.item(0).name+' ('+Math.round(this.files.item(0).size/1024)+'Кб)')" />
                <label for="file" class="inputfile"><span class="glyphicon glyphicon-download-alt"></span>Выберите файл</label>
            </div>
            <button type="submit">Загрузить </button>
        </form>
    </div>

    И код со cтороны бакенда (html-код изображения аватарки возвращается в #avatar):

    function onAvatarUpdate()
    {
    
        $validator = Validator::make(
            $form = Input::all(), [
                'avatar' => 'nullable|image|max:4000',
            ]
        );
    
        if ($validator->fails()) {
            throw new ValidationException($validator);
        };
          
        $user = Auth::getUser();
        $user->avatar = Input::file('avatar');   
        $user->save();
        return ['#avatar' => '<img src="'.$user->getAvatarThumb($size = 120,['mode' => 'crop']).'" alt="'.$user->name.'" height="120" width="120" class="img-circle">'];       
    }

    Всё достаточно тривиально. Если дальше развить идею, то можно пойти дальше - расширить функционал плагина Rainlab.User и внедрить обработку загрузки аватарки непосредственно в обработчик onUpdate формы (смысла правда в этом особо нет)

    • Koresh ответили на это сообщение.

      Linkonoid Спасибо! Для меня актуально - я ещё эту задачу не решил.
      Буду пробовать.

      Linkonoid Большое вам СПАСИБО!
      Код рабочий - аватарку загружает, в системную таблицу БД записывает правильно.

      А можете подсказать куда отправляется этот return

      return ['#avatar' => '<img src="'.$user->getAvatarThumb($size = 120,['mode' => 'crop']).'" alt="'.$user['name'].'" height="120" width="120" class="img-circle">'];

      Где его ловить?
      Я впервые пробую работать с аяксом октября. Читаю доку по дата атрибутам, вроде всё просто и понятно, но не догоняю с этим ретурном.
      Сейчас мне нужно перезагружать страницу чтобы увидеть новый аватар. Аватар у меня выведен просто тегом img
      Подскажите пожалуйста, как его правильно обновить чтобы не перезагружать страницу?

      • reazzon ответили на это сообщение.

        Появился новый вопрос: А как теперь правильно реализовать удаление аватаров?

        В html добавил кнопку Удалить аватар.
        Теперь как я понимаю нужно добавить функцию примерно такого содержания:

        function onAvatarDelete()
        {
            $user = Auth::getUser();
            
            // тут каким то образом нужно удалить аватар из БД и файлы аватара
            
            $user->save();
            return ['#avatar' => '<img src="/img/avatar/default.png" alt="'.$user['name'].'" class="img-circle">'];
        }

        Если не ошибаюсь, для этого должна быть отдельная форма <form>...</form>, у которой submit это и есть кнопка Удалить аватар.

        Помогите с удалением аватаров.

        • reazzon ответили на это сообщение.
          • Изменено

          Koresh Помогите с удалением аватаров.

          У юзера аватарка работает через AttachOne, то-есть обычная связь, которую нужно просто отвязать с помощью метода detach.

          $user->avatar()->detach();

          В твоем случае:

          function onAvatarDelete()
          {
              $user = Auth::getUser();
              $user->avatar()->detach();
              $user->save();
              return [
                  '#avatar' => '<img src="/img/avatar/default.png" alt="'.$user->name.'" class="img-circle">'
              ];
          }

          И да, в return массиве $user->name надо, а не $user['name'], потому-что $user это объект, а не массив.

          • Koresh ответили на это сообщение.

            Koresh Я впервые пробую работать с аяксом октября. Читаю доку по дата атрибутам, вроде всё просто и понятно, но не догоняю с этим ретурном.

            Я инструкцию писал по этой теме читать тут

            Koresh Могу добавить, что 90% возможностей CMS описано в документации (правда не всегда понятно написано, даже на языке первоисточника). А так лучше всегда смотреть исходники - там точно всё есть (заодно будут понятны все "подводные камни" при использовании). Что касается Ajax в OctoberCMS: на крупных проектах использовать крайне не рекомендуется по причине безопасности - eval(.....) (это касается всего кода в файлах страниц в контексте PHP - рекомендуется пользоваться для построения логики страниц только компонентами + в контексте Javascript следует избегать использования 'data-' атрибутов, т.е. использование 'data-' атрибутов это зло "вдвойне", по причине использования вызовов eval() как в PHP, так и в Javascript).
            Вот подборка полезных ссылок для разработчиков использующих Ajax-фреймворк OctoberCMS:

            • Koresh ответили на это сообщение.

              reazzon В твоем случае:

              Сделал так. Вылазит вот такая ошибка в алёрте:

              "Call to undefined method October\Rain\Database\QueryBuilder::detach()" on line 2483 of /vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php

              При этом в консоле выпадает POST 500

              Может у меня нет на форме каких то дата-атрибутов для этого? Вот моя форма:

              <form data-request="onAvatarDelete" >
                  <button type="submit">Удалить аватар</button>
              </form>

                Koresh Можно попробовать: $user->avatar = null;

                Вот код из плагина Rainlab.User, касающийся avatar (по умолчанию null):

                    public $rules = [
                        'email'    => 'required|between:6,255|email|unique:users',
                        'avatar'   => 'nullable|image|max:4000',
                        'username' => 'required|between:2,255|unique:users',
                        'password' => 'required:create|between:4,255|confirmed',
                        'password_confirmation' => 'required_with:password|between:4,255',
                    ];
                    public $attachOne = [
                        'avatar' => \System\Models\File::class
                    ];
                • Koresh ответили на это сообщение.
                  • Изменено

                  Linkonoid Можно попробовать: $user->avatar = null;

                  Так аватар обновляет аяксом на дефолтный, но после перезагрузки страницы аватар снова появляется. В таблице system_files БД запись с аватаром остаётся.

                  Вот функция:

                  function onAvatarDelete()
                  {
                      $user = Auth::getUser();
                      // $user->avatar()->detach();
                      $user->avatar = null;
                      $user->save();
                      return [
                          '#avatar' => '<img src="/img/avatar/default.png">'
                      ];
                  }
                  • Linkonoid ответили на это сообщение.

                    Koresh Может в кэше висит? А так файл, да - останется, т.к. обнуляется только привязка. Можно попробывать: $user->avatar = null; $user->avatar->save(); так \System\Models\File обновится.

                    • Koresh ответили на это сообщение.

                      Linkonoid Спасибо! Ваниль это хорошо, но нужно какое то время чтобы освоится. Я двумя руками за ваниль, поэтому полюбому к этому вернусь. Мне jQuery - это только палки в колёса.

                      • Linkonoid ответили на это сообщение.

                        Linkonoid
                        А эта строчка нужна: $user->avatar()->detach(); или нет? Я связей никаких не добавлял. Всё по дефолту и как Вы показали.

                        Koresh Всё что решается на jQuery, можно в 80% случаев решить на нативном javascript (зачастую даже кода меньше будет), остальные 20% - это "плюшки" jQuery, без которых можно (но дольше) обойтись, да и экосистема плагинов очень развита - а это большой плюс для быстрого прототипирования функционала.

                        • Koresh ответили на это сообщение.

                          Koresh Сделал так. Вылазит вот такая ошибка в алёрте:

                          Прошу прощения, ошибся методом.

                          не detach а delete

                          Исправленный пример:

                          function onAvatarDelete()
                          {
                              $user = Auth::getUser();
                              $user->avatar()->delete();
                              $user->save();
                              return [
                                  '#avatar' => '<img src="/img/avatar/default.png" alt="'.$user->name.'" class="img-circle">'
                              ];
                          }
                          • Koresh ответили на это сообщение.

                            reazzon не detach а delete

                            Исправленный пример:

                            Ура! Работает!!! Спасибо!

                            • reazzon ответили на это сообщение.
                              • Изменено

                              Koresh И кстати, только сейчас заметил. У тебя return возвращает вообще не обновление фрагмента.

                              Чтобы обновить фрагмент на странице надо делать так:

                              return [
                                  '#avatar' => $this->renderPartial($this.'::default-avatar', ['user' => $user])
                              ];

                              Где default-avatar это название фрагмента, который лежит в папке компонента рядом с файлом default.htm.

                              Где ['user' => $user] это мы передаем переменную $user в фрагмент, чтобы ты в верстке мог использовать
                              {{ user.name }}.

                              Опять-же, все это было в инструкции читать тут

                              • reazzon и Koresh ответили на это сообщение.

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

                                • Linkonoid ответили на это сообщение.