Simplify store controller method on laravel 5 - laravel

This is my store method to save a post.
public function store(CreatePostRequest $request)
{
$post = new Post([
'title' => $request['title'],
'content' => Crypt::encrypt($request['content']),
'published_at' => Carbon::now()
]);
$post->user()->associate(Auth::user());
$newPost=Post::create($post->toArray());
$this->syncTags($newPost, $request['tag_list']);
return redirect('posts')->withMessage('Post Saved Successfully !!!');
}
In laracasts tutorial he is just doing a
Article::create($request->all());
I need to do the extra stuff like encrypt, but am i cluttering the method? could it be cleaner?

Do it in the Model. I use the set/get*Attribute() method to change stuff on the fly.
So you could use Article::create($request->all()); then in the model use the fillable array to only autofill what is allowed (such as title, content and published_at).
then use something like (in the model)
function setContentAttribute( $value ){
$this->attributes['content'] = Crypt::encrypt($value);
}
In fact you could also adapt this approach so that the published_at attribute is set to today, or even better use your database to provide now()s time.

Related

Returning a Modal with relationships after creating in Laravel

I have a modal for example User. The User has relationships to Country and Currency modal as well. I would like to return a JSON object of the User with their relationships. I can achieve that using the code below:
$user = User::create($request->all()); // request contains all the information to create the user
$userDetails = User::with('country', 'currency')->where('id', $user->id)->first();
return json_encode($userDetails);
It works. However, is there a better or more recommended way to achieve this? Thank you for your time.
I found out that creating a new Laravel Resource and using the following approach seems much cleaner:
// in UserResource.php:
public function toArray($request)
{
return [
'id' => $this->id,
'country' => $this->country, // relationship
'currency' => $this->currency // relationship
]
}
// In controller:
return new UserResource($user);
Thanks!

How does Laravel generate SQL?

I'm brand new to Laravel and am working my way through the Laravel 6 from Scratch course over at Laracasts. The course is free but I can't afford a Laracasts membership so I can't ask questions there.
I've finished Section 6 of the course, Controller Techniques, and am having unexpected problems trying to extend the work we've done so far to add a few new features. The course has students build pages that let a user show a list of articles, look at an individual article, create and save a new article, and update and save an existing article. The course work envisioned a very simple article containing just an ID (auto-incremented in the database and not visible to the web user), a title, an excerpt and a body and I got all of the features working for that. Now I'm trying to add two new fields: an author name and a path to a picture illustrating the article. I've updated the migration, rolled back and rerun the migration to include the new fields and got no errors from that. (I also ran a migrate:free and got no errors from that.) I've also updated the forms used to create and update the articles and added validations for the new fields. However, when I go to execute the revised create code, it fails because the SQL is wrong.
The error message complains that the author field doesn't have a default, which is true, I didn't assign a default. However, I did give it a value on the form. What perplexes me most is the SQL that it has generated: the column list doesn't show the two new columns. And that's not all: the values list is missing apostrophes around any of the string/text values. (All of the columns are defined as string or text.)
As I said, I'm completely new to Laravel so I don't know how to persuade Laravel to add the two new columns to the Insert statement nor how to make it put apostrophes around the strings in the values list. That hasn't come up in the course and I'm not sure if it will come up later. I was hoping someone could tell me how to fix this. All of my functionality was working fine before I added the two new fields/columns.
Here is the error message:
SQLSTATE[HY000]: General error: 1364 Field 'author' doesn't have a default value (SQL: insert into `articles` (`title`, `excerpt`, `body`, `updated_at`, `created_at`) values (Today in Canada, The ideal winter-beater, This car is the ideal winter-beater for the tough Canadian climate. It is designed to get you from A to B in style and without breaking the bank., 2020-02-15 17:37:54, 2020-02-15 17:37:54))
Here is ArticlesController:
<?php
namespace App\Http\Controllers;
use App\Article;
use Illuminate\Http\Request;
class ArticlesController extends Controller
{
public function index()
{
$articles = Article::latest()->get();
return view ('articles.index', ['articles' => $articles]);
}
public function show(Article $article)
{
return view('articles.show', ['article' => $article]);
}
public function create()
{
return view('articles.create');
}
public function store()
{
//Stores a NEW article
Article::create($this->validateArticle());
return redirect('/articles');
}
public function edit(Article $article)
{
return view('articles.edit', ['article' => $article]);
}
public function update(Article $article)
{
//Updates an EXISTING article
$article->update($this->validateArticle());
return redirect('/articles/', $article->id);
}
public function validateArticle()
{
return request()->validate([
'title' => ['required', 'min:5', 'max:20'],
'author' => ['required', 'min:5', 'max:30'],
'photopath' => ['required', 'min:10', 'max:100'],
'excerpt' => ['required', 'min:10', 'max:50'],
'body' => ['required', 'min:50', 'max:500']
]);
}
public function destroy(Article $article)
{
//Display existing record with "Are you sure you want to delete this? Delete|Cancel" option
//If user chooses Delete, delete the record
//If user chooses Cancel, return to the list of articles
}
}
Is there anything else you need to see?
It may be possible because of you don't have defined that column in fillable property, to use mass assignment you have to specify that columns.
Try after adding that columns in fillable property.
Laravel mass assignment
Hope this helps :)

Relationship User and children data using belongsTo

public function children()
{
return $this->belongsTo('App\User', 'parent_id', 'id');
}
$item = Item::where([
'status' => '1',
'id' => $id
])->first();
$user = User::where([
id=>Auth::id()
])->with('children')->get()
return view('item.list')->with(compact('item', 'user'));
How can I get item and authenticated user along with children and send it to view in one query or is there any other best practise.
From your code, it looks like Item is not related in any way to the User model. If that is the case, there is likely no reason to try to do this in one query, and likely not efficient or even possible. There could be some relation that I don't know of, but you are probably better off doing the two queries.. One for the $item:
$Item = Item::where([
'status' => '1',
'id' => $id
])->first();
And then one to load the children on the User object. This one is potentially a little different. If you are manually loading the User object for some reason, you can eager load the children:
$user = User::where([
'id' => $someId
])->with('children')->first();
If you are within the auth middleware, you don't need to re-load the user, it is already provided by Laravel. You can just load the children if they are not already there:
$user->load('children');
or, even easier, from your blade page, just call the load on the page:
$user->children();
You have the code correct for sending to your view:
return view('item.list')->with(compact('item', 'user'));

laravel 5.6 Eloquent : eloquent relationship model creation issue

in my controller i create an Eloquent Model Instance passign throug a relation. The model is loaded on controller's __construct, that's why is present a $this->store and not a $store.
public function index()
{
if (is_null($this->store->gallery)) {
$this->store->gallery()->create([
'title' => 'gallery_title,
'description' => 'gallery_description',
]);
}
$gallery = $this->store->gallery;
dd($gallery);
return view('modules.galleries.index', compact('gallery'));
}
Simply if a store's gallery is not present yet, let's create it.
The first time i print out my dd() is ALWAYS null, if i reload the page the dd() show correctly my gallery model.
The things is weird for me, seems like the first time the creation is done but not ready... I can work around but why this code doesn't work the first time?
Help is very appreciate.
Relationship codes: on gallery ....
public function store()
{
return $this->belongsTo(Store::class);
}
on store...
public function gallery()
{
return $this->hasOne(Gallery::class);
}
When using the $this->store->gallery()->create() method, the original method is not hydrated with the new value, you can simply do a
$gallery = $this->store->refresh()->gallery;
OR
$gallery = $this->store->load('gallery')->gallery;
if you want to make your code cleanner you can do that in your Store Model:
public function addGallery($gallery){
$this->gallery()->create($gallery);
return $this->load('gallery')->gallery;
}
And that in your controller:
$gallery = $this->store->addGallery([
'title' => 'gallery_title',
'description' => 'gallery_description',
]);
and voila ! You have your gallery ready to be used :)
It's the lazy load part of Eloquent. basicly, when you tested for it with is_null($this->store->gallery) it sets it to that value.
when you tried to recover it again, it did not do the DB query, it just loaded the value already present (null).
after creation you need to force reload the relation:
$this->store->load('gallery');
or
unset($this->store->gallery);
or
$gallery = $this->store->gallery()->get();

Laravel Coding Practice / Most Optimised Method to Store

Laravel documentation says one should store as follows:
public function store(Request $request)
{
// Validate the request...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
}
However, why not just as follows:
public function store(Request $request)
{
Flight::create($request->all());
}
The above example is quite easy, since it only has one field. But I imagine its rather tedious to do something with many fields and have to assign each one as opposed to just passing the whole $request as in the second example?
First option gives you better control as to what goes into new model. If you store everything from the request then user might inject fields that you don't want to be stored for a new model in your store method.
For example, your flight has column is_top_priority that is declared as fillable in your Flight model, but when creating new flight you want to set only name for you flight (and leave is_top_priority as null or maybe it has default value of 0 in your table). If you write Flight::create($request->all()); then user can inject <input name="is_top_priority" value="1"> and get advantage of your code.
That is why it is not recommended to use fill($request->all()). Use $request->only(...) or assign each needed field manually as provided in your first example.
For example your model have some fields like name, email, password,status and etc.
Request validate name, email and password and if you do this:
Flight::create($request->all());
Client can send with other fields status, but you change status manually. I do this:
Flight::create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'password' => $request->get('password'),
'status' =>config('params.flight.status.not_active'),
]);

Resources