Можно ли в классах методом __toString() вернуть null?

Статус
В этой теме нельзя размещать новые ответы.

WMix

герр M:)ller
Партнер клуба
1 сервис решает 1 проблему
пусть так будет, но у тебя он решает много проблем.
ты просто думаешь, что проблема сохранить / удалить это проблема,
а правильно думать, что разобрать request это одна проблема, удалять файлы это другая проблема.

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

WMix

герр M:)ller
Партнер клуба
ну те я так бы делил
PHP:
class UserImageController{
    private $imagesService;
    public function uploadAction($request){
        $this->imagesService->craeate(File::fromRequest($request->file->get('image')));
    }
}

class ImagesService{
    private $storage;
    private $database;
    public function create($file){
        $fileId = $this->database->craeate($file->getName());
        $fileName = $fileId . "." . $file->getExtension();
        $this->storage->putFile($fileName, $file->getData());
    }
}
 

StalkerClasses

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


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

StalkerClasses

Новичок
ну те я так бы делил
PHP:
class UserImageController{
    private $imagesService;
    public function uploadAction($request){
        $this->imagesService->craeate(File::fromRequest($request->file->get('image')));
    }
}

class ImagesService{
    private $storage;
    private $database;
    public function create($file){
        $fileId = $this->database->craeate($file->getName());
        $fileName = $fileId . "." . $file->getExtension();
        $this->storage->putFile($fileName, $file->getData());
    }
}
У меня сервис обрабатывает данные для строки модели

$model = new Post;
$model->name = '';
$model->image = сервис:
$model->save();
 

WMix

герр M:)ller
Партнер клуба
Это нужно в конструктор перенести?
конечно, внедрение зависимостей это третья проблема. - когда я буду тестить, я захочу туда виртуальный стораж подставить.
У меня сервис обрабатывает данные для строки модели
модель это то что я назвал File::fromRequest, и сам Сервис и $database все конечно абстрактно, но смысл в том, чтоб сервис можно было использовать не только в HTTP но и в консольном приложении. ты же завязал его на POST
 

StalkerClasses

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

модель это то что я назвал File::fromRequest, и сам Сервис и $database все конечно абстрактно, но смысл в том, чтоб сервис можно было использовать не только в HTTP но и в консольном приложении. ты же завязал его на POST
Хорошо - переделаю покажу что получилось. + я думаю может имеет смысл этот сервис разбить на методы?


->setFile()
->setFieldDelete()
->setDevaultVaöue()
->setPathSave (Disk, path
->result()

И отказаться от конструктора
 

StalkerClasses

Новичок
Просто не знаю как его собрать этот сервис - у меня картинки загружаются в 4 местах и каждый раз что то изменив в сервисе придётся менять в 4 местах.
 

WMix

герр M:)ller
Партнер клуба
И отказаться от конструктора
это будет ошибкой. ты усложняешь себе интерфейс


ты продолжаешь думать классами

->setSaveDisk
->setPathSave

попробуй обьектами

$userImagesService = new ImagesService('/images/users/', '/var/www/images/users/', ... );
$productImagesService = new ImagesService('/images/products/', '/var/www/images/products/', ... );

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

StalkerClasses

Новичок
это будет ошибкой. ты усложняешь себе интерфейс


ты продолжаешь думать классами

->setSaveDisk
->setPathSave

попробуй обьектами

$userImagesService = new ImagesService('/images/users/', '/var/www/images/users/', ... );
$productImagesService = new ImagesService('/images/products/', '/var/www/images/products/', ... );

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


PHP:
<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
use App\Services\FileAttachDetachService;

abstract class BaseController extends Controller
{
    /** @var App\Services\FileAttachDetachService */
    public $fileAttachDetachService;

    public function __construct()
    {
        $this->fileAttachDetachService = new FileAttachDetachService();
    }
}
PHP:
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\BaseController;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use App\Http\Requests\AdminPostStoreRequest;
use App\Http\Requests\AdminPostUpdateRequest;
use App\Models\Post;
use App\Services\FileAttachDetachService;
use App\Utils\FrontendUility;

/**
 * Контроллер - управление постами
 *
 * GET|HEAD     |admin/post                 |admin.post.index
 * GET|HEAD     |admin/post/{post}          |admin.post.show
 * GET|HEAD     |admin/post/create          |admin.post.create
 * POST         |admin/post                 |admin.post.store
 * GET|HEAD     |admin/post/{post}/edit     |admin.post.edit
 * PUT|PATCH    |admin/post/{post}          |admin.post.update
 * DELETE       |admin/post/{post}          |admin.post.destroy
 * GET          |admin/post/{post}/delete   |admin.post.delete
 *
 */
class PostController extends BaseController
{
    /**
     * Редактировать пост (форма)
     *
     * @param \App\Models\Post $post
     * @return \Illuminate\View\View
     */
    public function edit(Post $post)
    {
    }

    /**
     * Обновить пост (процесс)
     *
     * @param \App\Http\Requests\AdminPostUpdateRequest $request
     * @param \App\Models\Post $post
     * @return \Illuminate\Routing\Redirector
     */
    public function update(AdminPostUpdateRequest $request, Post $post)
    {
        $post->post_type = $request->input('post_type') ? $request->input('post_type') : null;
        $post->name = $request->input('name');
        $post->parent_id = $request->input('parent_id');
        $post->description = $request->input('description');
        $post->sorting = intval($request->input('sorting'));

        // Логотип: загрузка (отсоединение) 1 файла
        $post->logo_image = $this->fileAttachDetachService->oneFile(
            $post->logo_image,
            'logo_image',
            'site/post/logo'
        );

        // Зарисовка: загрузка (отсоединение) 1 файла
        $post->figma_image = $this->fileAttachDetachService->oneFile(
            $post->figma_image,
            'figma_image',
            'site/post/figma'
        );

        // Изображения: загрузка нескольких картинок (в базу не пишем)
        $post->post_images = $this->fileAttachDetachService->manyFiles(
            $post->post_images,
            'post_images',
            'site/post/' . $post->id
        );

        if ($post->save()) {
            $request->session()->flash('flash_messages_success', 'Пост [' . $post->id . '] успешно обновлен');

            // Сохранение и к просмотру
            if ($request->input('redirect') == 'show') {
                return redirect()->route('site.post', $post->id);
            }

            return redirect()->route('admin.post.edit', $post->id);
        }

        $request->session()->flash('flash_messages_error', 'Ошибка обновления поста [' . $post->id . ']');
        return redirect()->route('admin.post.edit', $post->id)->withInput();
    }
}
PHP:
<?php

namespace App\Services;

use http\Exception;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Storage;

/**
 * Обработка сохранения/удаления файла для модели
 *
 * Загрузка файла(ов) на диск, возврат имени файла или null для записи в БД
 * Примеры:
 *
 * -- HTML --
 *
 * <!-- One file-->
 * <input type="file" name="logo_image[upload]">
 * <input type="checkbox" name="logo_image[delete]" value="1">
 *
 * <!-- Multi files -->
 * <input type="file" name="images[upload][]" multiple>
 * <input type="input" name="images[sorting][{{ md5($filename) }}]" value="1">
 * <input type="input" name="images[sorting][{{ md5($filename) }}]" value="2">
 * <input type="checkbox" name="images[delete][{{ md5($filename) }}]" value="1">
 * <input type="checkbox" name="images[delete][{{ md5($filename) }}]" value="1">
 *
 * -- PHP --
 * $model = new Post;
 * $model->name = 'foo';
 * $model->logo_image = $this->fileAttachDetachService->oneFile($model->logo_image, 'logo_image', 'site/post', 'public');
 * $model->images = $this->fileAttachDetachService->manyFiles($model->images, 'images', 'site/post', 'public');
 * $model->save();
 */
class FileAttachDetachService
{
    /** @var \Illuminate\Support\Facades\Request */
    private $requst;

    /** @var \Illuminate\Support\Facades\Storage */
    private $storage;

    /**
     * Конструктор
     *
     * @return void
     */
    public function __construct()
    {
        $this->requst = new Request;
        $this->storage = new Storage;
    }

    /**
     * Проверка входящих аргументов для работы функции
     *
     * @param array $args
     * @return \Exception|void
     */
    public function checkArgs(array ...$args)
    {
        if ($args[0][1] === null) {
            throw new \Exception('');
        }
    }

    /**
     * Загрузка (удаление по галочке) 1 файла
     *
     * @param string|null $defaultValue
     * @param string $formFieldName
     * @param string $savePath
     * @param string $diskName
     * @return string|null
     */
    public function oneFile(
        string|null $defaultValue = null,
        string $formFieldName = 'image',
        string $savePath = 'site/dir',
        string $diskName = 'public'
    ) {
        // Проверка аргументов
        // $this->checkArgs(func_get_args());

        // $file->getClientOriginalName());
        // $file->getClientOriginalExtension();
        // $path = $request->file('logo_image')->store('site/post/logo', 'public');

        // Загрузка 1 файла
        if ($file = $this->requst::file($formFieldName . '.upload')) {
            $fullFilePathAndName = $this->storage::disk($diskName)->putFile($savePath, $file);
            return $fullFilePathAndName;
        }

        // Удаление 1 файла (удаление идет по проверке!)
        if (intval($this->requst::input($formFieldName . '.delete')) === 1) {
            if ($this->storage::disk($diskName)->delete($defaultValue)) {
                return null;
            }
        }

        // Возврат старого значения
        return $defaultValue;
    }

    /**
     * Загрузка (удаление по галочке) нескольких файлов файла
     *
     * Дополнительно:
     * - Загрузка новых файлов
     * - Сортировка файлов
     * - Замена файлов
     * - Удаление файлов (по галочке)
     *
     * @param string|null $defaultValue
     * @param string $formFieldName
     * @param string $savePath
     * @param string $diskName
     * @return string|null
     */
    public function manyFiles(
        string|null $defaultValue = null,
        string $formFieldName = 'images',
        string $savePath = 'site/dir',
        string $diskName = 'public'
    ) {
        // Проверка аргументов
        // $this->checkArgs(func_get_args());

        $result = [];
        if (!empty($defaultValue)) {
            $defaultValueArray = explode(chr(10), $defaultValue);
            if (is_array($defaultValueArray) && !empty($defaultValueArray)) {
                foreach ($defaultValueArray as $value) {
                    $result[md5($value)] = $value;
                }
            } else {
                $result[md5($defaultValue)] = $defaultValue;
            }
        }

        // Загрузка файлов
        if ($files = $this->requst::file($formFieldName . '.upload')) {
            foreach ($files as $file) {
                $fullFilePathAndName = $this->storage::disk($diskName)->putFile($savePath, $file);
                $result[md5($fullFilePathAndName)] = $fullFilePathAndName;
            }
        }

        // Удаление файлов (удаление идет по проверке!)
        if ($filesDelete = $this->requst::input($formFieldName . '.delete')) {
            foreach ($filesDelete as $md5FileDelete => $valueConfirm) {
                if (key_exists($md5FileDelete, $result) && intval($valueConfirm) === 1) {
                    if ($this->storage::disk($diskName)->delete($result[$md5FileDelete])) {
                        unset($result[$md5FileDelete]);
                    }
                }
            }
        }

        // Сортировка файлов
        if ($filesSorting = $this->requst::input($formFieldName . '.sorting')) {
            foreach ($filesSorting as $md5FileSorting => $valueSorting) {
                if (key_exists($md5FileSorting, $result) && intval($valueSorting) > 0) {
                    // TODO
                }
            }
        }

        // Возвращаем результат
        if (count($result) > 0) {
            return implode(chr(10), $result);
        }

        return null;
    }
}
 

StalkerClasses

Новичок
Единственное что не знаю насколько правильно при использовании Dinj использовать вызов как статики:
if ($files = $this->requst::file($formFieldName . '.upload')) {
 

AmdY

Пью пиво
Команда форума
PHP:
/** @var App\Services\FileAttachDetachService */
    public $fileAttachDetachService;
Неужели так сложно хотя бы php освоить, прежде чем свои костыли начинать лепить?
Потом ещё можно освоить гугл и найти medialibrary для Laravel или тебя у них испугал #StandWithUkraine ?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
и зачем тут отдельно "модель для доступа", что тебе мешало public function getImage():?string прям в твоем "value object" вписать?
да и не value object это а entity, тк явно идентичность просматривается
если за 20 лет ты прочитал десятки обсуждений этой темы, и все-равно продолжаешь задавать этот вопрос, что тут можно ответить?
прочти, наконец, первые главы Банды 4х, value object не должен заниматься преобразованием данных, это прямое следствие принципов SOLID
 
Последнее редактирование:

StalkerClasses

Новичок
PHP:
/** @var App\Services\FileAttachDetachService */
    public $fileAttachDetachService;
Неужели так сложно хотя бы php освоить, прежде чем свои костыли начинать лепить?
Потом ещё можно освоить гугл и найти medialibrary для Laravel или тебя у них испугал #StandWithUkraine ?
Видел эту библиотеку. Мне она пока не пригодилась.
 

StalkerClasses

Новичок
PHP:
/** @var App\Services\FileAttachDetachService */
    public $fileAttachDetachService;
Неужели так сложно хотя бы php освоить, прежде чем свои костыли начинать лепить?
Потом ещё можно освоить гугл и найти medialibrary для Laravel или тебя у них испугал #StandWithUkraine ?
Я не представляю сколько мне нужно было бы написать кода что бы сделать

Загрузку
Обновление картинки
Сортировку
Удвоение

А так у меня это для каждой модели решается 1 строчкой кода для 1 поля
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
то что ты написал - это не модель, а просто говнокод

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

StalkerClasses

Новичок
то что ты написал - это не модель, а просто говнокод

модель - это то, что реализует бизнес-логику, без самих данных
бизнес-логики в картинках обычно нет, и здесь тебе скорее всего нужен сервис безо всяких приведений null к строке, но задачи я не знаю, могу лишь предполагать
Т.е. вот эта строчка говнокод - я понял.
Код:
        // Изображения: загрузка нескольких картинок
        $post->post_images = $this->fileAttachDetachService->manyFiles(
            $post->post_images,
            'post_images',
            'site/post/' . $post->id
        );
Говнокода было бы еще больше если бы я использовать библиотеку Laravel для работы с файлами.
У меня сервис просто отдает данные для поля поля модели.

Есть файл (кантрика) - он возвращает для нее путь.
Нет файла (картинки) - он возвращает null.
 

WMix

герр M:)ller
Партнер клуба
начиная от сюда, все не правильно.
PHP:
public function __construct()
    {
        $this->requst = new Request;
        $this->storage = new Storage;
    }
1. почему ты хочешь назвать это FileAttachDetachService, но опираешься на абстрактный Request а не на конкретный File
(проблему разбора Request не решил)
2 если new вынести наружу то я смогу подставить любой обьект, который подходит под тип
PHP:
public function __construct(File $file, Storage $storage){
     $this->requst = $request;
     $this->storage = $storage;
}
3. зачем знать снаружи
PHP:
$x->manyFiles(
        "defaultValue"
        // это говно
        ,
        string $formFieldName = 'images',
        string $savePath = 'site/dir',
        string $diskName = 'public'
    ) {
почему не просто $конкретный_обьект->manyFiles( "foo");
ты не можешь создать $конкретный_обьект?, который инкапсулирует
PHP:
        private string $formFieldName = 'images',
        private string $savePath = 'site/dir',
        private string $diskName = 'public'
ты не решил ни одну из проблем, ни зависимостей ни запроса, ты все всунул в service
в то время как у сервиса задачи и записать в базу и в storage
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Т.е. вот эта строчка говнокод - я понял.
нет, чувак, говнокод здесь все строки, кроме строк с закрывающими скобками, начиная от смешения camel case с подчеркиваниями, нарушениями всех принципов программирования, и заканчивая незнанием даже операторов и приведения типов в строке
$post->post_type = $request->input('post_type') ? $request->input('post_type') : null;
 

StalkerClasses

Новичок
начиная от сюда, все не правильно.


1. почему ты хочешь назвать это FileAttachDetachService, но опираешься на абстрактный Request а не на конкретный File
(проблему разбора Request не решил)
2 если new вынести наружу то я смогу подставить любой обьект, который подходит под тип
PHP:
public function __construct(File $file, Storage $storage){
     $this->requst = $request;
     $this->storage = $storage;
}
3. зачем знать снаружи
PHP:
$x->manyFiles(
        "defaultValue"
        // это говно
        ,
        string $formFieldName = 'images',
        string $savePath = 'site/dir',
        string $diskName = 'public'
    ) {
почему не просто $конкретный_обьект->manyFiles( "foo");
ты не можешь создать $конкретный_обьект?, который инкапсулирует
PHP:
        private string $formFieldName = 'images',
        private string $savePath = 'site/dir',
        private string $diskName = 'public'
ты не решил ни одну из проблем, ни зависимостей ни запроса, ты все всунул в service
в то время как у сервиса задачи и записать в базу и в storage
У меня сервис не пишет в БД и решает минимум 4 задачи по обработке формы..
Работает двумя сценариями - для одиночной загрузки и для мультизагрузки: http://maptex.ru/pic
1 ничего не делает если нет загрузки (возвращает существующее значение)
2 загружает картинку если идет загрузка
3 удаляет картинку (по галочке если отмечено) - возвращает null
4 делает сортировку файлов

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