I'm new in Laravel, I want to know where is the correct place where define functions that query table from DB. In Model or in Controller?
Example:
public function insertUser($firstname, $lastname, $email) {
$user = new User();
$user->firstname = $firstname;
$user->lastname = $lastname;
$user->email = $email;
$user->save();
return $user;
}
The function above where I should declare? Models or Controllers?
Edit:
For example: I need to create a function that return male authors that live in USA and their books. I define AuthorController that use Author (Model). What's the right way to define this function? I write a function in my controller that accept gender and nation as arguments, like:
public function getAuthoursByGenderAndNation($gender, $nation) {
$authors = Author::with("books")->where("gender", "=", $gender)->where("nation", "=", $nation)->get();
return $authors;
}
Or I define a generic function that returns all authors with their books and then apply where clause on function that call this generic function? Like:
public function showAuthors(Request $request) {
$gender = $request->get("gender");
$nation = $request->get("nation");
$authors = $this->getAuthors()->where("gender", "=", $gender)->where("nation", "=", $nation)->get();
return view("authors", ["authors" => $authors]);
}
public function getAuthors() {
$authors = Author::with("books");
return $authors;
}
keep in mind that all application logics should be in controller, and all data operations should be in model. in your question insert user is a application logic, so you should place that on controller, but if you want to define how data is managed, place that method in model. For example, you want a model has ability to retrieve a collection with some condition, may be a user with female gender only so you can Access it via Modell::getFemale()
The function you mention, should be used within a controller. I would recommend that you get a grasp on how MVC works before you dive in Laravel.
Reading that may be useful to you
MVC Concept
Laravel Docs
PHP MVC Tutorial
As according to MCV recommendations.
M (model) should be fat and C (controller) should be thin.
you should write your all database transaction related code in model. Even you can create repositories for database queries.
Your controller should be thin, so you should write only logical code there, like calling model function.
Example:
UserController.php
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Requests\Request;
class UserController extent Controller {
use App\User;
protected $_user;
public function __construct(User $user) {
$this->_user= $user;
}
function saveUser(Request $request) {
$user->fill($request->all());
$user->save();
// or you can directly save by $user->create($request->all());
}
}
This is how you can directly fill data to your User model with $fillable attribute defined there as
$fillable= ['name','email','password'];
If you define your model under the conventions of Eloquent you can simply use the built in Eloquent methods to insert your user as demonstrated in the documentation.
https://laravel.com/docs/5.3/eloquent#inserting-and-updating-models
In the wider scope of your question: 'where to define functions that query the DB table'.
I would suggest typically defining these on the model and looking to make use of the structures provided by Eloquent, for example defining scoped queries on your model.
The code in your controller would then call methods on your model eg.
Model::create();
It also appears you are trying to insert users. I would strongly suggest you look into using Laravel's built in Authentication structures. You'll find these very powerful.
https://laravel.com/docs/5.3/authentication
Hope this helps get you started.
Related
I'd really like some pointers on the best practice to follow when it comes to putting DB and business login into the model and having the controllers call the relevant methods.
I have a model and a controller for 'articles', see below. This code works fine but the way I have it working concerns me. I seem to be passing object all over the place (needlessly?) and there is lack of uniform to my method calls.
Hopefully the code is self explanatory and one of you geniuses can tell me everything I'm doing wrong and send back an example of how it should be done right!
Thanks for any tips!
FYI - The $article->tags field is comma-delimited 'keywords,like,this'
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Article;
use App\Models\Category;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class ArticleController extends Controller
{
private $article;
public function __construct()
{
}
// SHOW ALL ARTICLES
public function index(){
// Fetch articles and paginate
$articles = Article::getPublicArticles()->latest()->paginate(9);
// Explode each article's tags to arrays
$articles = Article::tagsToArrayFromMany($articles);
return view('articles.index', [
'articles' => $articles
]);
}
// SHOW SINGLE ARTICLE
public function show(Article $article, $slug){
// Increment the number of views
$article->addView($article);
// Explode this article's tags to an array
$article->tagsToArrayFromOne($article);
// Fetch other articles
$other_articles = $article->getOtherPublicArticles($article->hex)->take(3)->get();
// Explode each article's tags to arrays
$other_articles = Article::tagsToArrayFromMany($other_articles);
// Load the view
return view('articles.show', [
'article' => $article,
'other_articles' => $other_articles
]);
}
And here's the model:
<?php
namespace App\Models;
use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Article extends Model
{
use HasFactory;
// Set route key name
public function getRouteKeyName(){
return 'hex';
}
// Accessor for retrieving and formatting 'created_at'
public function getCreatedAtAttribute($value){
return Carbon::parse($this->attributes['created_at'])->format('d/m/Y');
}
// Relationship to user
public function user(){
return $this->belongsTo(User::class, 'user_id');
}
// Get all public articls
public static function getPublicArticles(){
return Article::where('status', 'public');
}
// Explode tags to arrays for all articles
public static function tagsToArrayFromMany($articles = []){
foreach($articles as $key => $article){
if($article->tags){
$articles[$key]['tags'] = explode(',', $article->tags);
}
}
return $articles;
}
// Explode tags to arrays for one article
public static function tagsToArrayFromOne($article){
if($article->tags){
$article['tags'] = explode(',', $article->tags);
}
return $article;
}
// Add view
public static function addView(object $article){
$article->views = ($article->views + 1);
$article->save();
}
// Get other articles
public static function getOtherPublicArticles(string $hex){
return self::getPublicArticles()->where('hex', '!=' , $hex)->orderByRaw('RAND()');
}
This question would most likely be more suited on CodeReview but that aside.
My response is more of a comment than an answer, however, comments have character limits so leaving this here. This is also my opinion, there is a balance to be struck between perfectionism and pragmatism and developers have different opinions on where on the scale you should be in different situations.
There are a couple of books I would recommend for reference to guide you in future situations:
Clean Code by Uncle Bob
The Pragmatic Programmer
My reasons for recommending the above are that Clean Code provides goals to strive towards whilst The Pragmatic Programmer highlights that goals are great in isolation but not always productive.
We call ourselves programmers but we're more than that, we're problem solvers that just use code as our tool to do so. Our true goal is not to write beautiful code but to solve a problem. If we can simultaneously do both then fantastic but, a client doesn't care about the code we write they just want a solution.
My point being, aim to follow "best practices" but don't get too hung up on them and fall into the trap of analysis paralysis and bike-shedding. Get something that works and then refactor where required later.
Anyway ...
Ideally a controller acts soley as a conductor between a request and response. It should determine where data should be forwarded and therefore which models or services to use. Business logic, shouldn't bleed into a controller.
Your ArticleController isn't horrific, however, it could probably be 'improved' a little. Both of your actions request data from your Article model then use the response to make another request to the model. Instead just combine those requests into a single request.
So for example refactoring:
$articles = Article::getPublicArticles()->latest()->paginate(9);
// Explode each article's tags to arrays
$articles = Article::tagsToArrayFromMany($articles);
To:
$articles = Article::publishedWithTags()->latest()->paginate(9);
You could do similar with your show method. Again this is just my opinion and you could 'optimise' it further by combining the latest call into your method if you wanted.
I am not sure why you have choosen to define all your methods as static, that doesn't seem necessary in my opinion. For querying your models, consider using query scopes.
Additionally, rather than defining and calling an addView() method, consider using a model event.
I don't know where you are in your journey, however, I suggest and recommend watching the Laravel from Scratch series. It covers a lot of common situations and suggested best practices when it comes to using the Laravel framework.
I'm currently trying to use Laravel Relationships to access my achievements Model using User model, I use this relationship code:
public function achievements()
{
return $this->hasMany('App\Models\User\Achievement');
}
I can easily make some eloquent queries, however I can't access any method that I created there, I can't access this method:
class Achievement extends Model
{
public function achievementsAvailableToClaim(): int
{
// Not an eloquent query
}
}
Using the following code:
Auth::user()->achievements()->achievementsAvailableToClaim();
I believe that I am using this Laravel function in the wrong way, because of that I tried something else without using relationship:
public function achievements()
{
return new \App\Models\User\Achievement;
}
But that would have a performance problem, because would I be creating a new class instance every time I use the achievements function inside user model?
What would be the right way of what I'm trying to do?
it's not working because your eloquent relationship is a hasMany so it return a collection. you can not call the related model function from a collection.
you can var dump it on tinker to understand more what i mean.
You can use laravel scopes.Like local scopes allow you to define common sets of constraints that you may easily re-use throughout your application.
In your case you use this like, Define scope in model:
public function scopeAchievementsAvailableToClaim()
{
return $query->where('achivement_avilable', true);
}
And you can use this like :
Auth::user()->achievements()->achievementsAvailableToClaim();
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
I successfully created a global scope in Laravel and I want to query a relation model in the global scope. I have a Video model, a Category mode, and a VideoCategory pivot model and I want to access the category model using video model in the global scope, such as:
<?php
use Illuminate\Database\Eloquent\ScopeInterface;
use Illuminate\Database\Eloquent\Builder;
class DefaultScope implements ScopeInterface{
public function apply(Builder $builder)
{
$model = $builder->getModel();
$builder->whereHas('categories', function( $q ){
$q->where('language', 2);
});
}
public function remove(Builder $builder)
{
}
}
Is that a possible thing to do?
Short answer: don't do that.
How to? Use manual joins, but again, don't.
It will lead to errors, unexpected behaviour, and you will quit it as soon as you got it work.
Eloquent creates new query for a model in so many places, that using has is impossible, what you have already noticed, I suppose. Using manual joins would let you do that for given model, but would also break 90% of relation based features of Eloquent, ie. you couldn't use has or with towards this model etc.
Okay so I'm building a pretty large application in Laravel. Users manage their own virtual soccer teams. I have a users table then I have a teams table with team specefic things like name, level,and arena, etc. For the arena though I decided to add a arenas table and then add a arena_id column in the teams table instead of just adding the arena name to the teams table.
so here is the basic relantionship:
User hasOne Team
Team hasOne User
Team hasOne Arena
Arena hasOne Team
so if I wanted to get the arena for a user I call the method
$user = User::with('team')->where('username', '=', $username)->first();
$user->team->arena->arena_name;
and everything works fine; however I feel there is a much cleaner or simpler way of doing this. Is there or is this fine for the aplication?
There is nothing wrong with the way you are doing it. That is a perfectly good way of doing it for your needs. However something that might help is creating a getArenaFromUsername() method in the User model. Your User model would look something like this:
<?php
class User extends \Eloquent {
protected $fillable = [];
public function getArenaFromUsername($username)
{
$user = User::with('team')->where('username', '=', $username)->first();
return $user->team->arena->arena_name;
}
}
So then to get the arena name from a controller you just do:
$user = new User;
$arena = $user->getArenaFromUsername($username);
-----------------------------------------OR-----------------------------------------------
Or use dependency injection by doing the following in your controller using the same method we just created in the model:
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
then to use it you can use one line in any method in your controller like so:
$this->user->getArenaFromUsername($username);
These are all different ways of abstracting your query to make it more reusable and cleaner to call in your controller. Don't be afraid to make public methods in your model to call.
A couple things.
You can eager load the sub-relationship like so:
User::with('team', 'team.arena')...
You can also create an accessor function (http://laravel.com/docs/eloquent#accessors-and-mutators) on your User model to make it a first-class property on the User object:
// accessed via $user->arena
public function getArenaAttribute {
return $this->team->arena;
}
// accessed via $user->arenaName
public function getArenaNameAttribute {
return $this->team->arena->arena_name;
}