I have a litte question for you.
I'm using Laravel and I'm not sure which is the best way (and place) to save different models at same time.
For example:
When a user creates a "RecordSheet", I need to automatically create other models related to the RecordSheet model. Obviously I will create the RecordSheet model in his own controller:
class RecordSheetController extends Controller
{
public function store(){
RecordSheet::create([
.......
'user_id' => Auth::user()->id
]);
}
}
Where should I put the creation of the other models? In the same RecordSheetController?
class RecordSheetController extends Controller
{
public function store(){
DB:beginTranaction()
try{
$record = RecordSheet::create([
.......
'user_id' => Auth::user()->id
]);
ModelB::create([
.......
'recordSheet' => $record->id,
'user_id' => Auth::user()->id
]);
}catch(Exception $e)
{
DB:rollback();
}
DB:committ();
}
}
I'm not sure about thate since I suppose that RecordSheetController should be responsible only of "RecordSheet" models and not other models.
Any suggestion would be appreciated! Thanks everyone!
you can use Laravel Observers for this scenario, create a RecordSheetObserver and place your ModelB code in the created method
Laravel provides some built-in conventions for placement of your action or CRUD (Create - Read - Update - Delete) code.
Typically, you can put the related model action in the same method. To start, you can utilise the artisan command:
php artisan make:controller RecordSheetController --resource
This will add the standard methods to your controller. These methods tie into any resource methods you have in your routing, which follow standards for GET/POST/PUT/etc.
Once you have your controller set up, it is usually easiest and most readable to do your related action within the same method, so you don't have to go back and forth with the user from page to controller and back again. So:
public function store(Request $request){
// Add transactions as you wish
$record = RecordSheet::create([
.......
' user_id' => Auth::user()->id
]);
ModelB::create([
.......
'recordSheet' => $record->id,
'user_id' => Auth::user()->id
]);
}
You can certainly make sub functions within this, but the key is to perform this at one time for efficiency. If there are many repeatable sub functions with less related actions, it may be helpful to move this to other parts of your app. But for simple, directly related creation, it tends to be more readable to keep them in the same class.
Related
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();
I have setup Spark and I have created my custom view in Settings - Students (assume User object is actually a teacher). I have also created migration and model Student.
Now http://spark.app/settings/students returns the page successfully. At this point, I need to return data from backend. I investigated Spark\Http\Controllers\Settings\DashboardController#show - which is the method returning the 'settings' view, however this doesn't return any data to view using ->with('user', $user)
But, as mentioned in Docs, :user="user" :teams="teams" :current-team="currentTeam" already available out of the box.
Where and how does Spark returns these values to /settings? And How do I make my Student object available likewise?
Now, if I want to return my Student object to front-end, I have 2 choices.
1) edit Spark\Http\Controllers\Settings\DashboardController
2) I think Spark\InitialFrontendState is the place where Spark returns these objects user, teams, currentTeam. This approach is something I've seen for the first time to be honest and I didn't really understand how it works.
So how should I achieve in Spark, something as simple as :
return view('spark::settings')->with('student', $student); ?
Add a new route and set up your own Controller & own view
web.php
Route::get('/settings/students', 'SettingsStudentController#index');
SettingsStudentController.php
class SettingsStudentController extends Controller {
public function __construct() {
$this->middleware('auth');
}
public function index(Request $request) {
$user = Auth::user();
$student = STUDENTCLASS::whatever();
return view('yourstudentview', ['student' => $student , 'user' => $user]);
}
}
I am trying to split some code and let model handle all database stuff, such as create, retrieve etc.
At the moment all of that code was in the controller and I quickly realized that code is very messy so instead I wanted to do something like this:
public function incompletedEntity(EntityRequestPartial $request)
{
$partial_entity = EntityTrash::saveEntity();
}
And my model:
public static function saveEntity(Request $request)
{
$entity = new EntityTrash();
$entity->lat = $request->input('lat');
$entity->lng = $request->input('lng');
$entity->slug = $request->input('name');
$user_id = Auth::id();
$entity->name = $request->input('name');
$entity->type = $request->input('type');
$entity->email = $request->input('email');
$entity->tags = $request->input('tags');
$entity->slug = $user_id;
$entity->building_name = $request->input('building_name');
$entity->address = $request->input('address');
$entity->town = $request->input('town');
$entity->postcode = $request->input('postcode');
$entity->telephone = $request->input('telephone');
$entity->save();
}
However, I cannot call that function because I am not passing an argument from the controller, what is the right approach for this? Should I do $request->input in controller and assign it to an array maybe? and deal with it in the controller?
If you wish to split out the code so that controllers don't touch models, I would recommend that you look into the repository pattern.
The idea behind this would be that in your controller you have
$this->entityRepository->create($data);
You could either pass in an array, or each individual property
$this->entityRepository->create($lat, $lng, $name, $type..etc);
This way, your controller can retrieve all of the data and validate it.
$data = $request->only(['lat', 'lng', 'name', 'type', 'email', 'tags', ..etc]);
$validator = Validator::make($data, ['name' => ['required']]);
Obviously you can use request validation or whatever you're comfortable with.
This way, your controller is responsible for receiving data and validating it. It can then pass it on blindly to the repository knowing that it is valid, and trusting that the repository will do its job.
As a side note, I highly recommend that you do not have your models interact with the Request object directly. The Illuminate\Http\Request class belongs to the HTTP layer, and the model belongs to the persistence layer. These two layers should never know of the existence of the other.
Generally speaking, your model should never depend on the Request object. Models can be used for any access of a database, or other storage area. Many of those access points may not be HTTP at all.
Your HTTP controllers should always be used to pass information from an HTTP request to a deeper layer of your application (e.g. a Model). So if you are using the Request object anywhere, it should be in your HTTP controllers.
I think it's important for you to review why the controller layer is there and why we don't just have Models and Views.
I would recommend you to take a look at the official Laravel Documentation first: https://laravel.com/docs and the Controller / Eloquent Model chapters. Moreover, Adam Wathans talk Github from the current Laracon is a good read for further refactoring.
First, make use of the CRUD pattern from the Laravel Controller in addition with the Eloquent Model. Then pass the validated data to your model via the create/save method.
<?php
namespace App\Http\Controllers;
use App\EntityTrash;
use App\Http\Controllers\Controller;
class EntityTrashController extends Controller
{
public function create(EntityRequestPartial $request)
{
return EntityTrash::create($request->only(['lat', 'lng', 'name', 'type', 'email', ...]));
}
}
Finally, maybe the repository pattern can improve your code as well.
I'm unsure of the best practice when inserting mass assignment relationships within Laravel 5.4 - I'm new to the framework. The below code is working correctly, however can you tell me is there a way to simply into one line (inserting relationships)?
I've tried to look at 'save()'and 'push()' but it's not working as expected. Would this have an impact if transactions would scale up?
I have a Listing model, with a hasOne relationship:
public function garage()
{
return $this->hasOne('App\Garage', 'post_id');
}
First of all I have some validation, then I use the following to store, which I want to simplify to one one line of code:
public function store(Request $request)
{
// Validation has passed, insert data into database
$listing = Listing::create($request->all());
$listing->Garage()->create($request->all());
}
Also if I wanted to return the data inserted, how would I do this as the following is only returning the Listing model and not the Garage relationship? Yes I know that I wouldn't do this in a real world application.
return \Response::json([
'message' => 'Post Created Succesfully',
'data' => $listing
]);
Any help is muchly appreciated
Method chaining
Your store method looks good. You could chain methods though, if you don't need the listing object itself:
public function store(Request $request)
{
// Validation has passed, insert data into database
$garage = Listing::create($request->all())
->garage()->create($request->all();
}
But if you need the listing object, it's fine as you did it before!
Returning relation models
If you want to return the garage relation model too, you can simply do that by accessing it like a normal class propery:
return \Response::json([
'message' => 'Post Created Succesfully',
'data' => [$listing, $listing->garage]
//Access collection of garage relation ship
]);
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.