I am currently working on a web app that has been set up using the Repository/Service Layer Design Pattern, i.e. I have service layer that does any necessary business logic before running any methods within the repository. I have facades for each one of my models which access their respective service layers, and this has been fine for the most part. However, now that I am trying to set up Eloquent relationships, the facades seem to be causing a massive headache as I am not sure which direction I should be going.
Take the following code:
class Account extends Eloquent {
// Our table name
protected $table = "accounts";
// Our primary key
protected $primaryKey = "id";
/**
* Role Relationship
*
* Returns a list of roles associated with
* this account
*/
public function roles() {
return $this->hasMany('Role');
}
}
This will not work as is, because instead of using the entity class of Role, it is using the Role Facade. I have figured out a workaround for this, by setting an alias for the Entity with a slightly different name, such as RoleEntity so that
public function roles() {
return $this->hasMany('RoleEntity');
}
will work, however this doesn't seem like the most optimal solution.
My question is, is the practice ok? Or better yet, should this be happening at all? And if not, how do I fix it/where did I go wrong?
You have two classes with the same name in the same namespace. Use different namespaces so you can use the same class names.
I usually use \Models to locate my models classes.
At the top of each model file:
namespace Models;
In your controller or any part of your app:
\Models\Role::first();
Note that changing the namespace on your model will require you to add the namespaces of other classes i.e. Str, Eloquent, Url, Redirect, etc.
use Eloquent;
use URL;
In your model, you also have to pass the namespaces in the relationship functions, i.e.:
public function roles() {
return $this->hasMany('\Models\Role');
}
Related
I have 3 data models, one of which extends the other:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Opinion extends Model
{
public function reactions()
{
return $this->morphMany('App\Models\Reaction', 'reactable');
}
...
}
namespace App\Models\Activity;
use App\Models\Opinion;
class ActivityOpinion extends Opinion
{
...
}
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Reaction extends Model
{
public function reactable()
{
return $this->morphTo();
}
...
}
The App\Models\Opinion model has a polymorphic relationship with the App\Models\Reaction model. I can retrieve all of the App\Models\Opinion reactions no problem, so I know the relationship works great.
My question is, how can I retrieve the same set of reactions from the App\Models\Activity\ActivityOpinion model? Because right now, it is looking for App\Models\Activity\ActivityOpinion as the relationship but I need it to look for App\Models\Opinion. Is it possible to mock another model in a polymorphic relationship?
This is because in a Polymorphic Relationship in the stored data (if leaved as default) the relationship type gets the class namespace (sort of) to specify wich model needs to be returned. That's why when you try to access to your reactions() relationship from ActivityOpinion it will look up for the App\ActivityOpinion value in the reactable_type.
You can customize the morph class to search in the model addind this:
Opinion.php
protected $morphClass = 'reaction';
This should be enough, if not, add it also in the ActivityOpinion model.
Note
This could breake some things when trying to search results using Eloquent. Check this other answer in order to address this possible inconviniance.
Update
I've just found out that you could do all this even easier with MorphMap. From the docs:
Custom Polymorphic Types
By default, Laravel will use the fully qualified class name to store
the type of the related model. For instance, given the one-to-many
example above where a Comment may belong to a Post or a Video,
the default commentable_type would be either App\Post or
App\Video, respectively. However, you may wish to decouple your
database from your application's internal structure. In that case, you
may define a "morph map" to instruct Eloquent to use a custom name for
each model instead of the class name:
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'posts' => 'App\Post',
'videos' => 'App\Video',
]);
You may register the morphMap in the boot function of your
AppServiceProvider or create a separate service provider if you
wish.
Assume, I have a model "Post" and I create two resource controllers for it - User/PostController and Admin/PostController.
So when I wish to consume the resource, my routes would look something like this:
/user/post/:id
/admin/post/:id
Is this correct according to the convention or am I doing it wrong?
It is recommended to use a separate model for its controller and vice versa. This will allow you to maintain consistency and readability in the application, keeping the application easy to development.
In the application where there are admin and user accounts, I suggest using Middleware + (roles + permissions) and do not stand out in routing /user/ and /admin/ a apply general /user/
In the my example I described, you can use one method in the PostController, for example: show, add permission (eg user-post-list, admin), add roles (e.g. admin, user), in the routing describe who has permission (eg user-post-list, admin) to given function (show) and assign: permissions to role, roles to user - one or many role to user. You can also add roles to the routing and assigning the appropriate permissions in controller.
You can load permissions from the file using fixtures, for example permissions.csv, roles.csv, role_permission.csv or use seeds and fixtures or only seeds - before make create roles and permissions
- create the appropriate migrations)
Second solution (and only using Seed) it not flexible, because enlarging the list of permissions and roles involves modifying the code as opposed to the first solution - loading them from the file.
Of course, there are other solutions. I think that the proposing by me has a simple logic, it is effective and flexible to changes.
Check this: https://itsolutionstuff.com/post/laravel-56-user-roles-and-permissions-acl-using-spatie-tutorialexample.html
or
https://www.google.pl/search?q=laravel+roles+and+permissions+tutorial&oq=laravel+roles+&aqs=chrome.4.69i57j69i60j69i65j0l3.11595j0j7&sourceid=chrome&ie=UTF-8
Here is how I go about this problem in Laravel, when having users with different access-types.
Lets say we have a model like you, called Post. Now what we will do is, add a scope to that model, which we'll define a bit further down here:
use App\Scopes\AdminScope;
class Post extends Model {
// Apply a global scope to this controller
protected static function boot(){
parent::boot();
static::addGlobalScope(new AdminScope);
}
In the router you define it as a regular resource route:
Route::resource('posts', 'PostsController');
In the Controller, you can fetch all Posts on the index method like normal. This will after we create our admin scope, return all the posts in the system for admin users, and those belonging to a specific user for regular users:
class PostsController extends Controller {
public function index(){
$posts = Post::all();
}
No comes the part where you differentiate wether returning all the posts in the system, or just the ones belonging to the current user, based on the user type that is logged in:
Create a new folder in your app-folder named Scopes. In this folder, create a new file called AdminScope.php which will look something like this:
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Auth;
class AdminScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
// IF THE CURRENT USER TYPE IS NOT ADMIN, ALTER THE QUERIES:
if( Auth::user()->type != "admin" ){
$builder->where('user_id', '=', Auth::user()->id)
}
}
}
Ofcourse, this last file you will need to alter to meet the requirements on how you differentiate between a normal user and an administrator.
The good thing about this approach, is that you now can apply this Scope to any model where you see it fit, and it will alter all the queries for non-admin users to only show the Models that they own where the scope is applied.
Note:
This is a global scope, that will apply to all the Eloquent Models where it has been added, and to all queries made for that model. If you want, you can also write conditional local scopes, which you can read more about here:
Local Scopes
In my Laravel 5.1 App I have a lot of aux Models with the same structure. I was thinking in the posibility of make one model and controller for using all of them, but I cannot figure how to do.
I explain, all the database aux tables have the fields ID and name, and are made for CRUD operations and for filling the forms all over the App.
Is possible to specify the table on the methods implemented by Laravel? I mean, stablish the table on construct, on get(), etc. This would made the work a much more simple if I could do AuxTable::create("sex") or even in requests like $request->auxtable("studies")->get().
Am I explaining?
you can do it with single model like below in Model class there is a method called setTable($table) which can set the table name you want to use so consider below
use Illuminate\Database\Eloquent\Model;
class AuxTable implements Model {
//other class properties
}
in your controller use the model like below
class SampleController extends BaseController {
public function index() {
$model = new AuxTable;
$model->setTable('sex');
$model->get();
}
}
this should do the trick
I'm reworking a project on Laravel 5.1
What I realize is that the old classes have become much complicated and do not really follow the 'single responsibility' principle anymore.
So I'm planning to do such:
<?php
class User extends Model
{
}
class SocialUser extends User
{
}
So I have a few questions,
Is it possible to achieve that?
If yes, then does the SocialUser class link back to the same database table which is Users and would it conflict with the User model itself?
Is this all a good design practice at the first place? Or I better make use of traits?
Thank you.
What you’re doing (extending the User model) is perfectly fine, and an approach I use myself in projects.
For example, if an application I’m building has shop-like functionality, then I may create a Customer model that extends my User model, and contains say, order-related relations:
class Customer extends User
{
public function orders()
{
return $this->hasMany(Order::class, 'customer_id');
}
public function worth()
{
return $this->orders()->sum(function ($order) {
return $order->total();
});
}
}
In a recent project, I’ve been working on email campaign functionality and created a Recipient class that extends the User model to add campaign-related methods:
class Recipient extends User
{
public function campaigns()
{
return $this->belongsToMany(Campaign::class, 'recipient_id');
}
}
Because both of these classes extend the User model, I get all of those (and Eloquent) methods:
$customers = Customer::with('orders')->get();
So long as you set the table in your base User model, any classes that inherit it will use that same table, even though the model may be named differently (i.e. Customer, Recipient, Student etc).
IMHO I would go for the Repository pattern. It make's a lot of sense in your situation.
I would do the following:
interface UserRepository {
public function find($id);
public function getAll();
public function create(array $attributes);
public function destroy($id);
//you get the point
}
class CoreUserRepository implements UserRepository
{
//implement the interface rules
}
class SocialUserRepository extends CoreUserRepository
{
//implement the specific logic related to a SocialUser
}
Update
As Mjh described in the comments simply implementing the interface on all UserTypeRepository caused repetition - probably not what you want!
By extending your CoreUser you avoid repetition & maintain a design that will work for your situation.
Although, in your case it could be argued that you are still following SRP because everything in the User model is relating to a user, it's only the type of user which is differing.
Why go for the Repository Pattern?
You are ensuring you have a contractual agreement that all User
Repositories need to implement.
Code is easier to maintain.
Business and data access logic can be tested separately
Should you extend your User model?
Here you are in danger of model pollution. While you can do anything with a model - not everything is a good idea.
Defining relationships on this approach would be a headache due to the confusion caused.
Everytime I'm writing a Laravel model it just gives me a feeling of messy code. I have relationships and other model functions specially when using domain driven design. So I though about separating relationships and functions.
Example I have a User class that extends Eloqeunt:
class User extends Eloquent{}
and inside this class I have register functions and password hashing functions etc. Also, we can declare the relationships so:
class User extends Eloquent{
function post(){
return $this->hasMany('POST');
}
}
For some reason this smells funky to me. My solution was to create a Entities folder and inside create a User folder which will hold 2 files one would be UserRelationship which would hold of the the relationships for this class:
class UserRelationship extends Eloquent{
function post(){
return $this->hasMany('POST');
}
}
and a second which would be the actual User class where I would write all of the functions and this class would extend the UserRelationship class instead of Eloquent:
class User extends UserRelationship{
public static function register($email, $password, $activate_token)
{
$user = new static(compact('email', 'password', 'activate_token'));
$user->raise(new UserWasRegistered($user));
return $user;
}
}
What do you guys think of this approach I am relatively new to all this so I don't know if this is bad practice or to much work for little reward. What do you guys recommend?
For a user model, it is too much work. The easiest way and still a better approach is to define the relationship in the user model. If for example it is a post model where you have relationships for post to "user, comment, reply etc" then you can attempt splitting your relationships