Laravel Mutators and Accessors - laravel

I have created mutator date function in model to convert the created_at date into human readable time using diffForHumans().
I have done the following
public function setDateAttribute($value){
return \Carbon\Carbon::parse($value)->diffForHumans();
}
It works fine but it affects in all. it is possible to apply this mutator only on the specified function of the controller rather than all function

A small logic in the mutator would do the job:-
public function setDateAttribute($value){
if ( request()->path() === "/yourURL/To/IndexMethod"){
return $this->attributes['date'] = \Carbon\Carbon::parse($value)->diffForHumans();
} else {
return $this->attributes['date'] = $value;
}
}
the idea is to check by the url.
getting request path helper
EDIT
Comparison using route name is better, as exact path can contain id/slugs.
public function setDateAttribute($value){
if ( \Route::currentRouteName() === "/yourRouteName/To/IndexMethod"){
return $this->attributes['date'] = \Carbon\Carbon::parse($value)->diffForHumans();
} else {
return $this->attributes['date'] = $value;
}
}
getting route name

Related

Which design pattern helps me in this case using - Laravel

so I have a controller which returns a view with a DB Collection which I have a repository,
now, in case I have a request I want to return an array - json and not a view
any ideas if this is the correct approach?
public function index(
Request $request,
CampaignPerformanceRepository $campaignPerformanceRepository
) {
$data = $campaignPerformanceRepository->getDataByPeriod($request);
if ($request->all()) {
return $campaignPerformanceRepository->getDataByPeriod($request);
}
return view('reports.campaign-performances', compact('data'));
}
I do something similar in cases where I may want to return a view or just json based on the request. There's an easy helper function request()->wantsJson() which you can use.
$data = $campaignPerformanceRepository->getDataByPeriod($request);
if ($request->wantsJson()) {
return $campaignPerformanceRepository->getDataByPeriod($request);
}
return view('reports.campaign-performances', compact('data'));

Laravel dynamic scope only works first time

I'm missing something with how the global scopes work in Laravel 5.5.
In my controller, index , I am passing filters into a getter:
public function index(SaleFilters $filters)
{
return new SaleCollection($this->getSales($filters));
}
getSales:
protected function getSales(SaleFilters $filters)
{
$sales = Sale::with('office')->filter($filters);
return $sales->paginate(50);
}
protected function range($range)
{
$dates = explode(" ", $range);
if (count($dates) == 2) {
$this->builder = Sale::with(['office', 'staff'])
->where('sale_date', '>=', $dates[0])
->where('sale_date', '<', $dates[1])
->orderBy('sale_date', 'desc');
return $this->builder;
}
return false;
}
I have a scope setup in the sale model as such, which I would have thought would apply to the above filter automatically ? If not, do I have to reapply the same scope, duplicating the scope code in the filter ?
protected static function boot()
{
parent::boot();
$user = Auth::user();
if (($user) && ($user['office_id'])) {
return Sale::ofOffice($user['office_id'])->get();
}
}
public function scopeOfOffice($query, $office)
{
return $query->where('office_id', $office);
}
So basically, IF the user has an office_id applied, it should apply the ofOffice scope, therefore it should only ever return the sales that apply to that office_id.
Basically it works on page load via axios GET request
Route::get('/sales', 'SalesController#index')->middleware('auth:api');
axios
.get('api/sales/?range=" + this.rangeFilter)
rangeFilter is basically a start and end date passed into the above filter query.
Can anyone shed some light on how the scopes really work or if anything is obvious as to why its not always working? As I said, it works on page load where I provide default values for the rangeFilter, however when I change those days and it refetches via the same axios call, it seems to not be applying the scope, and I get ALL results instead of where office_id = 'x'
As far as i'm concerned, the range filter above would be executing on the first page load as well, so not sure why it would apply there, and not afterwards.
You should not mix the use of dynamic scope with global one. Also, static boot function does not expect a return. In order to use dynamic scope, you need to call it every time you need it. Hence, the name is dynamic. Query applied is not always executed by default. There so,
protected function getSales(SaleFilters $filters)
{
$sales = Sale::ofOffice($anyOfficeHere)->with('office')->filter($filters);
return $sales->paginate(50);
}
To suit your existing code, you may want to add an if statement in your model. Then call the scope function without argument.
public function scopeOfOffice($q)
{
if (($user = \Auth::user()) && ($office = $user->office_id)) {
$q->where('office_id', $office);
}
}
// Your controller
protected function getSales(SaleFilters $filters)
{
$sales = Sale::ofOffice()->with('office')->filter($filters);
return $sales->paginate(50);
}
If you feel so much cumbersome to type ofOffice repeatedly. A global scope is the way to go. Within your model static boot function, you can also apply anonymous function if you feel creating a separated class kinda bloat your apps.
protected static function boot()
{
parent::boot();
static::addGlobalScope('officeOrWhatNot', function ($q) {
if (($user = \Auth::user()) && ($office = $user->office_id)) {
$q->where('office_id', $office);
}
});
}
// Your controller. No more `ofOffice`, it's automatically applied.
protected function getSales(SaleFilters $filters)
{
$sales = Sale::with('office')->filter($filters);
return $sales->paginate(50);
}

Modify all attributes of a Laravel model

Accessors will do their job on a single attribute perfectly, but I need a way to have a method to do an Accessor/Getter job on all attributes and automatically.
The purpose is that I want to replace some characters/numbers on getting attributes and then printing them out. I can do it from within controller and manually but I think it would be great to have it from model side and automatically.
Like overriding getAttributes() method:
public function getAttributes()
{
foreach ($this->attributes as $key => $value) {
$this->attributes[$key] = str_replace([...], [...], $value);
}
return $this->attributes;
}
But I have to call it every time on model $model->getAttributes();
Any way to do it automatically and DRY?
Try something like:
public function getAttribute($key)
{
if (array_key_exists($key, $this->attributes) || $this->hasGetMutator($key)) {
if($key === 'name') return 'modify this value';
return $this->getAttributeValue($key);
}
return $this->getRelationValue($key);
}
It's fully overriding the default method so be a bit careful.
EDIT
Also check out: http://laravel.com/docs/5.1/eloquent-mutators
I would go with following approach and override the models __get method:
public function __get($key)
{
$excluded = [
// here you should add primary or foreign keys and other values,
// that should not be touched.
// $alternatively define an $included array to whitelist values
'foreignkey',
];
// if mutator is defined for an attribute it has precedence.
if(array_key_exists($key, $this->attributes)
&& ! $this->hasGetMutator($key) && ! in_array($key, $excluded)) {
return "modified string";
}
// let everything else handle the Model class itself
return parent::__get($key);
}
}
How about running it with each Creating and Updating events. So you can do something like that:
public function boot()
{
Model::creating(function ($model)
return $model->getAttributes(); //or $this->getAttributes()
});
Model::updating(function ($model)
return $model->getAttributes(); //or $this->getAttributes()
});
}

Laravel cookie in serviceprovider not comparable

I try to pass a variable based on a cookie value in my compose function to all my view to build my menu, with the use of serviceproviders recommmended here:
File: Providers/ViewComposerServiceProvicer.php
public function boot(Request $request) { $this->composeTopBar($request);}
public function composeTopBar(Request $request)
{
$cookieValue = $request->cookie('brand');
// if value not set use default value.
if($cookieValue == null)
{
$cookieValue = 1;
}
$brands = \App\Brand::orderBy('priority', 'asc')->get();
foreach($brands as $brand){
if($brand->id == $cookieValue){
$brand->menuActive = true;
}
else{
// show value to debug
$brand->menuActive = $cookieValue;
}
}
view()->composer('front.layouts.top', function ($view) use ($brands) {
$view->with('brandItems',$brands );
});
}
the cookieValue looks like
yJpdiI6IlNJODBvQ1RNM004OWVleyJpdiI6IlNJODBvQ1RNM004OWVleyJpdiI6IlNJODBvQ1RNM004OWVl
While the value in my controller looks like '2' How can i get the original value 2 in my compose function?
I need to get the original value to compare it in my composeTopBar function so I can pass a variable to be true if it equals the cookie value.
Method to set cookie
$response = response()-> view('front.products.category', compact('products','category'));
$response->withCookie(cookie()->forever('brand',1));
return $response;
I ended up using a class based composer .
The reason why this works is because it's called later in the lifecycle of laravel and the Cookie variables are decrypted. When using Closure based composers the values are encrypted.
Try this: put the view() call as a parameter to response().
$response = response(view('front.products.category', compact('products','category')));
$response->withCookie(cookie()->forever('brand', 1));
return $response;

codeigniter passing count_all_results to view

I'm using the count_all_results() function to return a user's number of languages spoken. But when I try to pass the number to the view, I keep getting a php undefined variable (for $lang_cnt). Below is my code:
Model
function countLanguages($id) {
$this->db->where('user_id', $id)->from('languages');
return $this->db->count_all_results();
}
Controller
function showLangCount() {
$data['lang_cnt'] = $this->language_model->countLanguages($id);
$this->load->view('lang_view', $data);
}
View
<p>This user speaks <?php echo $lang_cnt; ?> languages.</p>
One problem is that your model function takes two arguments:
function countLanguages($id, $cnt_languages)
But when you call it you are only passing one argument:
$this->language_model->countLanguages($cnt_languages);
And an even bigger problem, as Rocket points out, is that countLanguages doesn't return anything. Try this:
function countLanguages($id) {
$this->db->where('user_id', $id)->from('languages');
return $this->db->count_all_results();
}
Always check your model functions if they return value or not. Try this:
function showLangCount() {
if($this->language_model->countLanguages($id))
{
$data['lang_cnt'] = $this->language_model->countLanguages($id);
}
else
{
$data['lang_cnt'] = NULL;
}
$this->load->view('lang_view', $data);
}
Its better to use:
return $query->num_rows();
to return the number of rows effected...

Resources