Share value from database in all view in laravel 5 - laravel-5

I'm new to laravel. I'm trying to create a simple app with some variable that shouold be in every view. So I create a setting model, with many fields and a unique slug field. Now to share this variable from database I've created a middleware:
public function handle($request, Closure $next)
{
$site_settings = Cache::remember('settings', 60, function() {
return Setting::all();
});
view()->share('site_settings', $site_settings);
return $next($request);
}
Now to show this variable in view I have:
{{{ $site_settings->get(0)->value }}}
This works great, but I'd like to have a more intuitive code in my view, accessing the setting by slug. Something like:
{{{ $site_settings->findBySlug("MYVARIABLE")->value }}}
So it's possible to filter collection by an unique slug?

Info:
This assumes your settings table structure is as following:
----------------------
| id | key | value |
----------------------
| 1 | titleĀ | foo |
| 2 | asd | bar |
| 3 | qqq | zzz |
----------------------
Steps
Step 1: Put the following newCollection method into App\Setting model:
class Settings extends ... {
public function newCollection(array $models = array())
{
return new \App\Collections\SettingsCollection($models);
}
}
Step 2: Put the following lines into App\Collections\SettingsCollection class:
<?php namespace App\Collections;
use Illuminate\Database\Eloquent\Collection;
class SettingsCollection extends Collection {
public function get($name, $default = null)
{
return array_first($this->items, function($itemKey, $model) use ($name)
{
return $model->key == $name;
}, $default)->value;
}
}
Step 3: Enjoy! This is the collection I'm currently using. Instead of this:
$settings = Setting::all();
$settings->where('key', 'key_name')->first()->value;
You can simply just do this:
$settings = Setting::all();
echo $settings->get('key_name'); // returns value of the setting with the key 'title'

Maybe a query scope would fit.
In your model:
public function scopeFindBySlug($slug){
return $query->where('slug','=', $slug);
}
So you will have: $site_settings->findBySlug('your_slug')->get();

Related

Laravel model observer method doesn't get fired

In my Laravel (7.x) I am trying to use Observers for updating device_inventories table if the value of the record if the record of the subscriber_devices table is updated as inactive.
device_inventories | subscriber_devices
----------------------- | --------------------------------
# | title | status | # | device_inventory_id | status
----------------------- | --------------------------------
1 | device-1 | active | 1 | 1 | active
2 | device-2 | active | 2 | 2 | active
For example:
Suppose I update the record id = 2 of subscriber_devices to status = inactive then the value of device_inventories / status should be updated to replacement. Assuming, that the device was damaged and needs to be sent for replacement.
AppServiceProvider.php
use App\SubscriberDevice;
use App\Observers\ObserverChangeDeviceInventoryStatusToReplacement;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
SubscriberDevice::observe(ObserverChangeDeviceInventoryStatusToReplacement::class);
}
}
App\Observers\ObserverChangeDeviceInventoryStatusToReplacement.php
use App\DeviceInventory;
use App\SubscriberDevice;
class ObserverChangeDeviceInventoryStatusToReplacement
{
public function updated(SubscriberDevice $SubscriberDevice)
{
DeviceInventory::where('id', $SubscriberDevice->device_inventory_id)->update([
'status' => 'replacement'
]);
}
}
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
self::where('token', $token)->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
The observer method doesn't seems to be firing. What am I doing wrong.?
Thanks to #EmptyBrain.
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
$self = self::where('token', $token)->first();
$self->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
Reference laravel Eloquent model update event is not fired

How use conditional relationship in eloquent laravel

I have a 'conversation_message' table and separate sender role by column 'sender_type' (admin/user). Both of admin & user in a different table. But when I call the model, that showed error Call to a member function addEagerConstraints() on null
Table column and data
| id | id_group | id_reply | id_sender | sender_type | message
| 1 | 1 | null | 3 | admin | Hi, I'm admin
| 2 | 1 | 1 | 3 | admin | I wanna give u promo
| 3 | 1 | 2 | 18 | user | What's promo ?
I've tried if conditional with column value, but it doesn't work.
Conversation_message.php
public function sender(){
switch($this->sender_type){
case "user":
return $this->belongsTo(User::class, 'id_sender', 'id');
case "admin":
return $this->belongsTo(Admin::class, 'id_sender', 'id');
default:
return;
}
}
InboxController.php
public function message_detail_chat($groupId){
$data = [];
$data['messages'] = Conversation_message::with('replies')
->with('sender')
->with('recipients')
->where(['id_group' => $groupId])->get();
}
I expect to use conditional model by column value 'sender_type' but the actual output is wrong.
laravel provide query scope your can read it from here https://laravel.com/docs/5.8/eloquent#local-scopes
function userSender(){
$this->belongsTo(User::class, 'id_sender', 'id');
}
function adminSender(){
return $this->belongsTo(Admin::class, 'id_sender', 'id');
}
public function scopeSender($query)
{
return $query
->when($this->sender_type === 'user',function($q){
return $q->with('userSender');
})
->when($this->sender_type === 'admin',function($q){
return $q->with('adminSender');
});
}
Now you can access your Sender like this
Conversation_message::Sender()->first();
This should give you the right Sender. Hope it helps.

Save Eloquent model with relation make two entries, why?

This is my controller method:
public function store(Request $request, $course_id){
$price['value'] = $request->price;
$price['desc'] = $request->desc;
If ($request->promo === "on") {
$price['promo'] = 1;
} else {
$price['promo'] = 0;
}
$course = Course::find($course_id);
Price::create($price)->courses()->save($course);
return redirect('/course/'.$course_id.'/edit');
}
For example if my request var have:
$request->price = 100
$request->desc = 'blablabla'
$request->promo = 'on'
After I submit the form I got two new rows with the same data:
Table prices:
id | price | desc | promo
1 | 100 | blablabla | on
2 | 100 | blablabla | on
Some info on models, I've:
public function courses () {
return $this->belongsToMany('App\Course', 'course_price');
}
in prices model and:
public function prices () {
return $this->belongsToMany('App\Price', 'course_price');
}
in courses model.
Whats wrong?
Try attach:
$price = Price::create($price);
$price->courses()->attach($course_id);
instead of:
$course = Course::find($course_id);
Price::create($price)->courses()->save($course);
it's looks like you make double post request on
Price::create($price)->courses()->save($course);
create and save.
please try your code with
Price::create($price)->courses();

Adding custom eloquent attribute in Laravel 5

I have a function named siblings which fetches all siblings of a user.
select siblings(id) as `siblings` from users where id = 1
I can access the function in Eloquent as
User::where('id', 1)->first([DB::raw(siblings(id) as `siblings`)]->siblings;
I want to make the siblings available via custom attribute.
I added siblings to $appends array
I also created getSiblingsAttribute method in my User model as
public function getSiblingsAttribute()
{
if (!$this->exists()) {
return [];
}
$siblings = User::where('idd', $this->id)
->first([DB::raw('siblings(id) AS `siblings`')])
->siblings;
return explode(',', $siblings);
}
But this is not working as $this->id returns null
My table schema is users(id, username,...), so clearly id is present.
Is there a way by which I can bind the siblings function while querying db and then returning something like $this->siblings from getSiblingsAttribute. If I can bind siblings(id) as siblings with query select globally as we do for scopes using global scope.
That way my code can be simply
public function getSiblingsAttribute()
{
return $this->siblings;
}
The simplest way is to create a view in your database and use that as a table:
protected $table = 'user_view';
Otherwise I need more information about your id == null problem.
If you can fix this by your own in the next step it is important that you use an other column name by selecting as in your accessor otherwise you run in an infinite loop.
public function getSiblingsAttribute()
{
if (!$this->exists()) {
return [];
}
$siblings = User::where('id', $this->id)
->first([DB::raw('siblings(id) AS `siblings_value`')])
->siblings_value;
return explode(',', $siblings);
}
EDIT
Sadly there is no simple way to archieve this.
But after a little bit tinkering I have found a (not very nice) solution.
Give it a try.
You have to add the following class and trait to your app.
app/Classes/AdditionalColumnsTrait.php (additional column trait)
namespace App\Classes;
trait AdditionalColumnsTrait {
public function newEloquentBuilder($query) {
$builder = new EloquentBuilder($query);
$builder->additionalColumns = $this->getAdditionalColumns();
return $builder;
}
protected function getAdditionalColumns() {
return [];
}
}
app/Classes/EloquentBuilder.php (extended EloquentBuilder)
namespace App\Classes;
use Illuminate\Database\Eloquent\Builder;
class EloquentBuilder extends Builder {
public $additionalColumns = [];
public function getModels($columns = ['*']) {
$oldColumns = is_null($this->query->columns) ? [] : $this->query->columns;
$withTablePrefix = $this->getModel()->getTable() . '.*';
if (in_array('*', $columns) && !in_array($withTablePrefix, $oldColumns)) {
$this->query->addSelect(array_merge($columns, array_values($this->additionalColumns)));
} elseif (in_array($withTablePrefix, $oldColumns)) {
$this->query->addSelect(array_values($this->additionalColumns));
} else {
foreach ($this->additionalColumns as $name => $additionalColumn) {
if (!is_string($name)) {
$name = $additionalColumn;
}
if (in_array($name, $columns)) {
if (($key = array_search($name, $columns)) !== false) {
unset($columns[$key]);
}
$this->query->addSelect($additionalColumn);
}
}
if (is_null($oldColumns)) {
$this->query->addSelect($columns);
}
}
return parent::getModels($columns);
}
}
after that you can edit your model like this:
class User extends Model {
...
use App\Classes\AdditionalColumnsTrait;
protected function getAdditionalColumns() {
return [
'siblings' => DB::raw(siblings(id) as siblings)),
];
}
...
}
now your siblings column will be selected by default.
Also you have the option to select only specific columns.
If you don't want to select the additional columns you can use: User::find(['users.*']).
Perhaps it is a solution for you.

How to check user is Admin or not in laravel blade view

I have
User table like
+==============+
| User |
+==============+
| id |
+--------------+
| firstname |
+--------------+
| lastname |
+--------------+
| email |
+--------------+
| password |
+--------------+
and my roles table
+==============+
| Roles |
+==============+
| id |
+--------------+
| name |
+--------------+
and my role_user table is
+=============+
| role_user |
+=============+
| user_id |
+-------------+
| role_id |
+-------------+
How can I check current logged user is admin or normal user?
You need to add roles relationship in your User model like so:
public function roles()
{
return $this->belongsToMany(App\Role::class);
}
and now you need to create isAdmin user like so:
public function isAdmin()
{
return in_array(1, $this->roles()->pluck('role_id')->all());
}
As 1 you put id of your admin role. Of course it could be also defined in other way, but everything depends on how this will be used.
It could be also defined this way:
public function isAdmin()
{
return $this->roles()->where('role_id', 1)->first();
}
and now in your Blade you can do:
#if (auth()->check())
#if (auth()->user()->isAdmin())
Hello Admin
#else
Hello standard user
#endif
#endif
It's not an ACL for this simple functionality you don't even need a database table roles you can add extra tinyInteger status column and add numbers for example:
0 = Disabled
1 = Visitor
2 = Admin.
To make it functional add following code to your User.php.
public function isDisabled ()
{
return $this->statusCheck();
}
public function isVisitor ()
{
return $this->statusCheck(1);
}
public function isAdmin ()
{
return $this->statusCheck(2);
}
protected function statusCheck ($status = 0)
{
return $this->status === $status ? true : false;
}
To check in blade template you can add
#if(Auth::user()->isDisabled())
You are not Active
#elseif(Auth::user()->isVisitor())
Welcome to example.com
#elseif(Auth::user()->isAdmin())
Welcome Admin
#endif
Moreover you can make blade custom directives, paste this code to your app/providers/AppServiceProvider.php in boot() method.
// Blade custom directives for isAdmin
Blade::directive('isAdmin', function() {
return "<?php if(Auth::user()->isAdmin()): ?>";
});
Blade::directive('endisAdmin', function() {
return "<?php endif; ?>";
});
// Blade custom directives for isVisitor
Blade::directive('isVisitor', function() {
return "<?php if(Auth::user()->isVisitor()): ?>";
});
Blade::directive('endisVisitor', function() {
return "<?php endif; ?>";
});
// Blade custom directives for isDisabled
Blade::directive('isDisabled', function() {
return "<?php if(Auth::user()->isDisabled()): ?>";
});
Blade::directive('endisDisabled', function() {
return "<?php endif; ?>";
});
To call this you use need to write following lines in your blade view
#isAdmin()
Welcome Admin
#endisAdmin
#isVisitor()
Welcome to example.com
#endisVisitor
#isDisabled()
Your are not active
#endisDisabled
In short laravel provides you a number of ways to solve a problem, it just depend on your need and application structure.
Role.php
use Illuminate\Database\Eloquent\Model;
class Role extends Model {
protected $fillable = [
'name'
];
/**
* A role can have many users.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users() {
return $this->belongsToMany('App\User');
}
}
Then you can add this to User model:
public function isAdmin()
{
foreach ($this->roles()->get() as $role)
{
if ($role->name == 'Admin')
{
return true;
}
}
}
View
#if(Auth::check())
#if (Auth::user()->isAdmin())
<h2>Admin user enter code here<h2>
#endif
#endif
the methods shared works. the problem is if you have to check more than once per page, it hits the database that many times. for instance, let's say you have a navigation with 8 links. the first, fourth, and seventh links should only be visible by admin only. that query will hit your database 3x. maybe i'm just anal but it's a duplicated request.
i'm trying to find another way to store a variable that loads once in the view/template so that every time i need to check if it's an admin, i check the variable and not hit the database every time. i've done it via controller -> view, but not just view alone in a template. i'm thinking of creating a helper method and returning an object to be checked once per page load.
So you have some field isAdmin if it is 1 for example user is admin if not it is not. When user is loged check with (Auth::user()->isAdmin == 1) then user is admin else it is not
with Auth::user()-> u can check any field from user table of current logged user.
Best Regards

Resources