Laravel: How to use function of relation - laravel

I'm facing a situation where a I have to call a function from the controller of a relation instance. For better explanation I will write an example below
I have a Article controller in which I have a preview() function.
A User can have multiple Article.
Let's say that the preview() function parse a text and replace special pieces of text by the user's name.
So my function will looks like this
//In ArticleController
public function preview(Article $article , User $user){
return str_replace("username", $user->name , $article->text);
}
But for a specific situation I want to display a preview of the article when I list all the users
So in UserController
public function index(){
foreach( User::all() as $user){
echo $user->articles[0]->preview( ... );
}
}
Obviously this piece of code will not work.
But I'm more looking of the way to proceed when I face this kind demand.
Should I create a Repository? Use this preview() function somewhere else? Or Is it just a bad practice to do that? What's the best approach or way of thinking when we face this?
Or maybe I'm just missing something important in Laravel's ORM. :/

I assume Article is a model. So you have to add hasMany relation to User (user has many articles). Inside article you have to add preview function. In this case you will be able to find $user->article (or user->articles) and run ->preview function. This is the easiest solution I guess.
You can also add custom attribute like getPreviewAttribute and append it to article model. This way you would have $user->article->preview.

Related

Laravel use query in creating event

what is best and clean way to insert a query value on creating event model like this (Laravel 5.7)
public static function boot() {
parent::boot();
self::creating(function ($model) {
$model->person_job = App\Job::where('person_name',$request->name);
});
}
i don't wanna put this routine query in my controller , so what do professional developers here?
You can use request() function(helper) anywhere in your project if you have no $request variable.
In MVC architecture logic must locate in Controller mostly, Here in Laravel, events mostly are used to do some specific logic(I think that logic mostly does not rely on a user's given data as you do here) on every model based on the event, for example, if you want to generate a unique_id for each Model on created || creating event, or doing caching or something like that.
See here for more about events.

Laravel create related Model view, pass prefilled value

i am implementing a backoffice system with many Models and Relations.
Now im stuck with my UI Stragegy:
Lets assume i have Houses and Rooms. One House has many Rooms.
I have created controllers for both Models the "Laravel" way.(Resource Controllers)
So i have routes for both of them
example.com/backoffice/house
example.com/backoffice/room
What i want to implement sounds simple:
I want an Button inside the Detail View of a House ("Create Room for this House") which redirects me to "room/create" but in the create view i want to set the value for "house_id" to the id of the House i am comming from. So i can normaly use the store method in the RoomController and then redirect back to the house.
I want a general way because i must use this function on many Models/Views. I am thinking about a session variable but i think eventually has someone a better way for generally handling such cases? Or a better idea for UI Handling?
Apparently, Laravel removed some of their awesome documentation for version 5.6, being nested resource controllers.
What you could do, is use nested routes.
Let's assume your current controllers are set up the following way:
Route::resource('houses', 'HouseController');
Route::resource('rooms', 'RoomController');
If you change this part to the following:
Route::resource('houses', 'HouseController');
Route::resource('houses.rooms', 'RoomController');
This couples every room to a house and is really easy to manage. It gives you URL's like houses/4/rooms/create, which gives you a house_id in your create method instantly:
public function create($houseId)
{
return view('houses.rooms.create', ['houseId' => $houseId]);
}
If you want to edit a room, it is exactly the same:
public function edit($houseId, $roomId)
The Laravel 5.1 documentation still has an example of this technique.
To do this, i would suggest the following way (there might be other ways also)
Change Route:
Route::get('room/create/{house_id?},'Controller#action')->name('room.create')
Add <a> tag in house_view.blade.php file.
Create Room for this House
Room Controller file.
public function formCreate($house_id)
{
return view('form.room_create', ['house_id' => $house_id]);
}
Add type hidden <input> tag in room_create.blade.php file
<input type="hidden" id="house_id" name="house_id" value="{{$house_id or ''}}">

How can I apply a status (condition) check direct to a model in laravel?

I have added a status column to my product table. Now I want that when I use Product::all() I get only the product with status is active. something like soft delete. I want that the model ignores the products that are passive until they are activated again. Is there a way to do this?
PS: I already used the model (Product::class) in tons of places. so i am looking a direct way that affects all the methods i made already. otherwise i will have to apply the solution to all the methods one by one.
If you're looking for exactly the same functionality as Soft Deletes, you can use global scopes. SoftDeletes trait uses global scope. So, if you'll apply global scope to Product model Product::all() will return all results where status = 1.
This is the best way IMO, create a scope in the model
function scopeActive($query) {
return $query->where('status', 1);
}
This way, you could do something like this
Product::active();
You can try it as:
Product::where('status', 1)->get()

Search object by slug and not by id

I'm a relative beginner with Laravel (using version 5.2.3) and have been working through tutorials on Laracasts and then doing a bit of my own experimenting.
I successfully set up a route that fetches an item from a table by its ID, as shown below
Route::get('/wiseweasel/{id}', 'WiseweaselController#singleArticle');
For simplicity, the controller simply dd's the article
public function singleArticle($id)
{
$article = ww_articles::find($id);
dd($article);
}
This works absolutely fine - I visit eg /wiseweasel/2 and get the contents of the record with id2.
So, I then wanted to use the slug field from the record instead of the id. Since I know the ID method was working, I've tried just modifying this route and controller (also tried creating anew, neither worked) So I now have:
Route::get('/wiseweasel/{slug}', 'WiseweaselController#singleArticle');
and
public function singleArticle($slug)
{
$article = ww_articles::find($slug);
dd($article);
}
The slug for the second record is "secondarticle". So, visiting the url /wiseweasel/secondarticle, I would expect to see the same record as previously dd'd out. Instead, I end up with null.
Even more oddly, using the original id route (/wiseweasel/2) still returns the record... when I have removed all trace of this from the routes and controller, so I would expect this to fail...
This is making me wonder if this could be some odd caching issue? I've tried
php artisan route:clear
in case the route was being cached. I've also tried restarting both Apache and MySql (I'm using XAMMP for both).
Still no luck though... not sure if I've misunderstood how something works or what's going on... so if anyone has any suggestions as to what I might have done wrong, or anything to try, I would be very grateful! :)
You also have the option of using Route Model Binding to take care of this and inject the resolved instance into your methods.
With the new implicit Route Model Binding you can tell the model what key it should use for route binding.
// routes
Route::get('/wiseweasel/{article}', 'WiseweaselController#singleArticle');
// Article model
public function getRouteKeyName()
{
return 'slug';
}
// controller
public function singleArticle(Article $article)
{
dd($article);
}
Laravel Docs - Route Model Binding
Laravel won't automatically know that for slug it should search record in different way.
When you are using:
$article = ww_articles::find($slug);
you are telling Laravel - find record of www_articles by ID. (no matter you call this id $slug).
To achieve what you want change:
$article = ww_articles::find($slug);
into
$article = ww_articles::where('slug', $slug)->first();
This will do the trick (for slug put the name of column in table in database). Of course remember that in this case slug should be unique in all records or you won't be able to get all the slugs.
Maybe it's a bit late for the answer but there is another way to keep using find method and use slug as your table identifier. You have to set the protected $primaryKey property to 'slug' in your model.
class ww_articles extends Model
{
protected $primaryKey = 'slug';
...
}
This will work because find method internally uses the getQualifiedKeyName method from Model class which uses the $primaryKey property.
If you have both routes like this
Route::get('/wiseweasel/{id}', 'WiseweaselController#singleArticle');
Route::get('/wiseweasel/{slug}', 'WiseweaselController#singleArticle');
it will always use the first one. Obviously, there is no id 'secondarticle', so it returns null (although in this case it doesn't matter, they both point to the same method).
The reason is route will search through possible routes till it finds a matching, which is always the one with {id}. Why? You're not telling Route that {id} must match an integer!
You can make sure {id} is understood as an integer, however I suggest using urls like this is a better option
/wiseweasel/{id}/{slug?}
Another suggestion. Do not use names such as xx_articles for a model, but Article instead. This way you can use the new implicit route binding. So using implicit route binding your url would look like this (assuming your model is called Article)
Route::get('/wiseweasel/{article}', 'WiseweaselController#singleArticle');

CodeIgniter 2 and usage of $this->

I'm using CodeIgniter 2 and have installed Ion Auth and also the News tutorial that comes with CodeIgniter.
In the News Controller, the element for the page title is written like this...
$data['title'] = 'Page Title';
However, in the Ion Auth Controller, the element for the page title is written like this...
$this->data['title'] = 'Page Title';
They both seem to work equally well, so can anyone explain the difference(s)? Maybe Ion Auth was written for an older version of CodeIgniter? Is there any practical reason why I'd want to use one over the other? Please link to sources as needed.
I guess it's the author's preference. He likes to use a class property to store the view's data. It allows him to share it across methods. If you look at the author's other projects (Source 1, 2, 3), you can see two examples (source 1 & 2 goes together).
On a side note, for your project, this could allow you to extend the Auth controller with more view data.
class MY_Auth extends Auth {
function __construct()
{
parent::__construct();
}
function index()
{
$this->data['foo'] = 'bar';
parent::index();
}
}
That would allow you to use the $foo variable to your authentication view. (/auth/index in this case.)
In my own projects, I like to use a protected property for my view's data. It does give you much more freedom than a local variable. You don't need to pass the view's data as an argument all the time and you can easily extend your controllers afterward.
Hope this helps!
if you are going to use this $this->data it means you can access $this->data through out the class methods. On the other hand if you are using $data it is only available for the current scope or method and if you need data some where else then you will have to pass it as parameters to the other methods.
Adding $this on the data variable, makes it to be accessible through the class.
I believe the $data or $this->data is only used for "View". It will be passed from the "Controller" to the "View", so we can access that variable through the "View".
So, there will be no differences on the "View" side.

Resources