M
M
Melodic2016-05-11 17:21:03
Yii
Melodic, 2016-05-11 17:21:03

Service layer in yii, what's the right way?

I am familiar with designing an application in yii2. I decided to abandon ActiveRecord (except for the admin panel) and divide everything into layers.
User model interface

<?php
namespace app\core\models\user;
use app\core\models\city\CityInterface;
use app\core\models\country\CountryInterface;

interface UserInterface
{
    /**
     * Возвращает ID пользователя
     * @return integer
     */

    public function getId();

    /**
     * Устанавливает ID пользователя
     * @param  $id
     */
    public function setId($id);

    /**
     * Возвращает имя
     * @return string
     */
    public function getFirstName();

    /**
     * Возвращает фамилию
     * @return string
     */
    public function getLastName();

    /**
     * Возвращает отчество
     * @return string
     */
    public function getPatronymic();

    /**
     * Возвращает город
     * @return CityInterface
     */
    public function getCity();

    /**
     * Возвращает страну
     * @return CountryInterface
     */
    public function getCountry();

    /**
     * Возвращает дату регистрации пользователя
     * @return \DateTime
     */
    public function getRegistrationDate();

    /**
     * Возвращает email
     * @return string
     */
    public function getEmail();

    /**
     * Возвращает установленный пароль
     * @return string
     */
    public function getPassword();

    /**
     * @param $firstName string Устанвалвивет имя пользователя
     */
    public function setFirstName($firstName);

    /**
     * @param $lastName string Устанавливает фамилию пользователя
     */
    public function setLastName($lastName);

    /**
     * @param $patronymic string Устанавливает отчество пользователя
     */
    public function setPatronymic($patronymic);

    /**
     * @param $email string Устанавливает email пользователя
     */
    public function setEmail($email);

    /**
     * @param $password string Устанаваливает пароль
     */
    public function setPassword($password);


}

User Interface Implementation
<?php
namespace app\core\models\user;
use yii\base\Model;
use yii\base\Object;
use app\core\models\city\CityInterface;
use app\core\models\country\CountryInterface;

class User extends Object implements UserInterface
{

    
    /**
     * @var $_id integer
     */
    private $_id;

    /**
     * @var $_firstName string
     */
    private $_firstName;
    /**
     * @var $_lastName string
     */
    private $_lastName;
    /**
     * @var $_patronymic string
     */
    private $_patronymic;
    /**
     * @var $_city CityInterface
     */
    private $_city;
    /**
     * @var $_country CountryInterface
     */
    private $_country;
    /**
     * @var $_registrationDate \DateTime
     */
    private $_registrationDate;
    /**
     * @var $_email string
     */
    private $_email;
    /**
     * @var $_password string
     */
    private $_password;


    /**
     * Возвращает имя
     * @return string
     */
    public function getFirstName()
    {
        return $this->_firstName;
    }

    /**
     * Возвращает фамилию
     * @return string
     */
    public function getLastName()
    {
        return $this->_lastName;
    }

    /**
     * Возвращает отчество
     * @return string
     */
    public function getPatronymic()
    {
        return $this->_patronymic;
    }

    /**
     * Возвращает город
     * @return CityInterface
     */
    public function getCity()
    {
        return $this->_city;
    }

    /**
     * Возвращает страну
     * @return CountryInterface
     */
    public function getCountry()
    {
        return $this->_country;
    }

    /**
     * Возвращает дату регистрации пользователя
     * @return \DateTime
     */
    public function getRegistrationDate()
    {
        return $this->_registrationDate;
    }

    /**
     * Возвращает email
     * @return string
     */
    public function getEmail()
    {
        return $this->_email;
    }

    /**
     * @param $firstName string Устанвалвивет имя пользователя
     */
    public function setFirstName($firstName)
    {
        $this->_firstName = $firstName;
    }

    /**
     * @param $lastName string Устанавливает фамилию пользователя
     */
    public function setLastName($lastName)
    {
        $this->_lastName = $lastName;
    }

    /**
     * @param $patronymic string Устанавливает отчество пользователя
     */
    public function setPatronymic($patronymic)
    {
        $this->_patronymic = $patronymic;
    }

    /**
     * @param $email string Устанавливает email пользователя
     */
    public function setEmail($email)
    {
        $this->_email = $email;
    }

    /**
     * @param $password string Устанаваливает пароль
     */
    public function setPassword($password)
    {
        $this->_password = $password;
    }

    /**
     * Возвращает ID пользователя
     * @return integer
     */
    public function getId()
    {
        return $this->_id;
    }

    /**
     * Устанавливает ID пользователя
     * @param  $id
     */
    public function setId($id)
    {
        $this->_id = $id;
    }
}

And there is a service for this model.
User model service interface
namespace app\core\models\user;
interface UserServiceInterface
{

    /**
     * Возвращает пользователя по его ID
     * @param $id integer ID пользователя
     * @return UserInterface
     */
    public function findById($id);

    /**
     * Возвращает пользователя по его E-mail
     * @param $email string Email пользователя
     * @return UserInterface
     */
    public function findByEmail($email);


    /**
     * Возвращает всех пользователей
     * @return UserInterface[]
     */
    public function findAll();

    /**
     * Сохраняет пользователя
     * @param UserInterface $user
     *
     */
    public function save(UserInterface $user);

    /**
     * Удаляет пользователя
     * @param UserInterface $user
     */
    public function delete(UserInterface $user);

}

and the implementation of this service interface
<?php


namespace app\core\models\user;


use yii\db\Command;
use yii\db\Query;

class UserService implements UserServiceInterface
{

    /**
     * Возвращает пользователя по его ID
     * @param $id integer ID пользователя
     * @return UserInterface
     */
    public function findById($id)
    {
        return $this->findOneByAttribute('id', $id);

    }

    /**
     * Возвращает пользователя по его E-mail
     * @param $email string Email пользователя
     * @return UserInterface
     */
    public function findByEmail($email)
    {
        return $this->findOneByAttribute('email', $email);
    }

    /**
     * Возвращает всех пользователей
     * @return UserInterface[]
     */
    public function findAll()
    {

        $query = new Query();
        $rows = $query->all();
        $users = [];
        foreach ($rows as $row) {
            $users = $this->load($row);
        }
        return $users;

    }

    /**
     * Сохраняет пользователя
     * @param UserInterface $user
     *
     */
    public function save(UserInterface $user)
    {
        $command = new Command();
        $columns = [
            'first_name' => $user->getFirstName(),
            'last_name' => $user->getLastName(),
            'patronymic' => $user->getPatronymic(),
            'email' => $user->getEmail(),
        ];

        if ($user->getPassword() != null) {
            $columns['password_hash'] = \Yii::$app->security->generatePasswordHash($user->getPassword());
        }
        if ($user->getId() == null) {
            $command->insert(UserActiveRecord::tableName(), $columns);
            $user->setId(\Yii::$app->db->getLastInsertID());
        } else {
            $command->update(UserActiveRecord::tableName(), $columns, ['id' => $user->getId()]);
        }
    }

    /**
     * Удаляет пользователя
     * @param UserInterface $user
     */
    public function delete(UserInterface $user)
    {
        $command = new Command();
        $command->delete(UserActiveRecord::tableName(), ['id' => $user->getId()]);
    }

    private function load($result)
    {
        $user = new User();
        $user->setId($result['id']);
        $user->setEmail($result['email']);
        $user->setFirstName($result['first_name']);
        return $user;
    }

    private function findOneByAttribute($attribute, $value)
    {
        $query = new Query();
        $row = $query->andWhere([$attribute => $value])->one();
        if ($row) {
            return $this->load($row);
        } else {
            return null;
        }
    }
}

Tell me, am I moving in the right direction? Feeling like something is wrong :)

Answer the question

In order to leave comments, you need to log in

3 answer(s)
M
mitaichik, 2016-05-11
@mitaichik

No, not in the correct one.
The service layer is the layer that stands above the business logic of any particular part of the application (or the entire application). The service provides the outside (other parts of the application\3rd party applications) with an interface to access that part of the application.
There is no question of any refusal from ActiveRecord here - this is about something completely different. The service can quite easily operate with ActiveRecord models, why not?
The fact that you replaced everything with getters.setters also has nothing to do with the service layer. Plus, Yii has a much more convenient setter getter mechanism - read the documentation.
Your UserService is not a service at all, but a pure repository. Yes, you can do them, but why can't he operate with the most convenient and flexible AR?
As for changing the selection of users - yes, the repository will help here, and the interface will help. But why can't the repository use AR, and the interface can't be hung on the model?
As for "what will happen, if this and that, we will change the framework, etc" - there will be no such thing! In all my practice, I have seen only 2 transitions - from yii1 to yii2, and from yii1 to java, that's all!
In short, yii is cool, no need to reinvent the wheel, you need to be imbued with his philosophy and catch enlightenment.
Yes, as for services, I advise you to read about the Service Locator and related topics in the documentation, it says there how best to implement them technically.

N
Nikita, 2016-05-11
@bitver

You write in Yii, so use the framework tools, what it is designed for. Now it looks like an attempt to do something of your own, well, write your own framework or use another one if you don't like Yii. No, I'm not defending the framework, this is the most obvious thing that comes to mind.
And about the organization of the Service Layer in Yii, it is already in the templates and Gii generates it (SearchModel) - this is a kind of layer between the model and the controller. It is not necessary to be tied to a specific model, you can create your own class from yii\base\Modelor yii\base\Objectand do whatever your heart desires with ready-made nice features in the form of events and other things.

M
Maxim Timofeev, 2016-05-13
@webinar

Why use a framework if you don't use it? I often come across the finalization of finished projects in yii and am amazed. When the framework is installed and then pure php is written, and it’s good if using OOP. What for? If it is not convenient to work with the framework, why use it? Does the client insist? Well then it is necessary to use it, instead of to pretend.
If you don't like yii - try laravel, symfony, write your own in the end, but don't talk like that. Moreover, as a rule, this is not dictated by the structure of the framework, but by its ignorance at the proper level.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question