Приведение Eloquent\Collection (Laravel) к массиву stdClass с использованием шаблона репозитория

Я пытаюсь реализовать шаблон репозитория в приложении Laravel 5, следуя этой статьи. В нем реализация репозитория преобразует объект для определенного источника данных (в данном случае Eloquent) в stdClass, чтобы приложение использовало стандартный формат и не заботилось об источнике данных.

Чтобы преобразовать один объект Eloquent, они делают это:

/**
* Converting the Eloquent object to a standard format
* 
* @param mixed $pokemon
* @return stdClass
*/
protected function convertFormat($pokemon)
{
    if ($pokemon == null)
    {
        return null;
    }

    $object = new stdClass();
    $object->id = $pokemon->id;
    $object->name = $pokemon->name;

    return $object;
}

Или, как указал кто-то в комментариях, это также может работать:

protected function convertFormat($pokemon)
{
    return $pokemon ? (object) $pokemon->toArray() : null;
}

Но что происходит, когда я хочу привести всю коллекцию объектов Eloquent к массиву ** stdClass **? Должен ли я перебирать коллекцию в цикле и приводить каждый элемент отдельно? Я чувствую, что это будет большим ударом по производительности, поскольку мне придется зацикливаться и приводить каждый элемент каждый раз, когда мне нужна коллекция чего-то, и это также кажется грязным.

Laravel предоставляет Eloquent\Collection::toArray(), который превращает всю коллекцию в массив массивов. Я полагаю, что это лучше, но все же не stdClass

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

echo $repo->getUser()->name;

Вместо того, чтобы делать это:

echo $repo->getUser()['name'];

person Vic    schedule 15.07.2015    source источник
comment
Эээээ! Массивы! Потеря подсказок типов, моделей и методов... Создайте собственный класс User, если вы не хотите раскрывать модель Eloquent, не используйте общие массивы.   -  person sisve    schedule 15.07.2015
comment
Интересное предложение. Звучит неплохо, но тогда каждый раз, когда мне нужна была новая модель, мне приходилось создавать сущность модели, интерфейс, репозиторий, который реализует этот интерфейс, а теперь также общий класс, который будет возвращен репозиторием? В любом случае, вся логика этого нового универсального класса User уже находится внутри репозитория, зачем нужен еще еще один уровень абстракции? В конце концов, мне просто нужно получить доступ к данным и универсального класса объекта для этого вполне достаточно, я думаю. Или этот пользовательский класс будет просто пустой оболочкой без методов и/или атрибутов? @SimonSvensson   -  person Vic    schedule 15.07.2015
comment
В ваших репозиториях должна быть логика только в отношении доступа к базе данных, того, как данные загружаются (и, возможно, сохраняются). Такие вещи, как $user-›setPassword('...'), являются частью вашей модели предметной области и не принадлежат вашему репозиторию. Но вам все равно потребуется вызов $repo-›persist($user), если только вы не реализуете какое-либо отслеживание объектов. Я бы сказал, что вы должны использовать Doctrine вместо Eloquent, но это более серьезное изменение, чем просто пропуск массивов. Во всяком случае, вы пытались использовать array_map для своего фактического вопроса? В чем проблема перебрать все?   -  person sisve    schedule 15.07.2015
comment
Ну, я подумал, может быть, это повлияет на производительность. Я также забыл о array_map. Это отлично работает. Теперь я думаю о том, что вы сказали, и это имеет большой смысл. У меня есть дополнительный вопрос: имеет ли смысл размещать логику модели предметной области на сервисном уровне (используя здесь сервис-ориентированную архитектуру)? Тогда мой UserService будет содержать бизнес-логику и будет единственным, кто будет иметь дело с репозиторием. Мое приложение просто вызывало бы Службу, и это сделало бы UserService тем общим классом User, о котором вы упомянули, не так ли?   -  person Vic    schedule 15.07.2015
comment
кстати, @SimonSvensson, вы можете поставить это array_map в качестве ответа, и я приму его (если с ним не связаны проблемы с производительностью).   -  person Vic    schedule 15.07.2015


Ответы (4)


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

person sisve    schedule 16.07.2015

Используя красноречие, вы можете сделать что-то вроде этого:

/**
 * Gets the project type by identifier.
 *
 * @param string $typeIdentifier
 *
 * @return object
 *
 */
public function getTypeByIdentifier($typeIdentifier)
{
    $type =  ProjectType::where(
        'type_identifier', $typeIdentifier
    )->first();

    return (object) $type->toArray();
}

Все мои фабрики и т. д. принимают stdClass, чтобы он был единым. В Eloquent вы можете сделать как указано выше, так как Eloquent уже имеет функцию toArray(), которая необходима для сериализации, но вы также можете легко расширить модель (Illuminate\Database\Eloquent), чтобы этот метод был доступен для всех ваших красноречивых моделей. Я предлагаю вам расширить модель, чтобы вы также могли автоматизировать эти коллекции, а не только отдельные записи.

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

person Keith Mifsud    schedule 05.11.2015
comment
Это решение работает лучше, чем другие для меня. - person Ricardo Vigatti; 19.04.2016

Вы можете сделать что-то вроде этого,

Например, есть класс User

$user = User::find(1)->toArray();

//this is code for convert to std class
$user = json_encode($user);
$user = json_decode($user);

json_decode по умолчанию возвращает объект stdClass.

Я надеюсь, это поможет.

person Jaykesh Patel    schedule 29.04.2016

Вы можете использовать метод getQuery() для преобразования /превратить \Illuminate\Database\Eloquent\Builder в \Illuminate\Database\Query\Builder.

return $this->model->getQuery()->get();

вернет коллекцию (или массив до 5.3) stdClass объектов.

return $this->model->where('email', $email)->getQuery()->first();

вернет объект stdClass.

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

person Paul Spiegel    schedule 10.08.2017
comment
как насчет того, если вы хотите загрузить отношения? Таким образом, нетерпеливые загруженные отношения будут потеряны - person Helder Lucas; 12.08.2017
comment
@HelderLucas Отношения даже не будут загружены. И я не видел, чтобы это было частью вопроса. Однако, если вам нужны отношения, вы либо жадно загружаете их самостоятельно, либо живете с накладными расходами красноречия. - person Paul Spiegel; 13.08.2017