Laravel Global Settings Model - php

I'm working on building a small CRUD app in Laravel 5. I have a few app wide settings such as "SiteTitle" and "BaseURL" I'd like to give my admins the ability to change from the UI. My whole app uses a base.blade.php template that gets extended to the different views+controllers, this is where these settings would most likely be used.
Something like:
<h1>{{ $setting->SiteName }}</h1>
I have the settings stored in a database table that are tied to a Setting.php model.
I'd rather not everyone of my controller methods query the database for these settings to just pass them up to the template base.blade.php.
What's the best way of creating some type of global setting variable I can reuse throughout the app?
Thanks in advance!

You could create a service provider, say SettingsServiceProvider, that loads all the settings from the database and then caches them. Then on subsequent page loads, it could return cached setting values rather than querying the database, which you should be rightfully concerned about.
Something as simple as:
class SettingsServiceProvider extends ServiceProvider
{
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton('settings', function ($app) {
return $app['cache']->remember('site.settings', 60, function () {
return Setting::pluck('value', 'key')->toArray();
});
});
}
}
Assuming your settings model is called Setting as per Laravel’s naming conventions. You can then access settings like so:
<h1>{{ array_get(app('settings'), 'site.name') }}</h1>
If you wanted a prettier way of accessing settings, you could create a helper function:
function setting($key)
{
return array_get(app('settings'), $key);
}
Which would make usage like this:
<h1>{{ setting('site.name') }}</h1>
Almost emulating the config() helper function’s usage.

If your app uses multiple controllers then you could define a parent controller that gets extended by all your other controllers. Within the parent controller's constructor you can retrieve the settings data and store it in a class property which will be available to all of the child controller classes. Your child controllers can simply pass this data to the view.

Related

Namespaced controllers and routes in laravel

Assume, I have a model "Post" and I create two resource controllers for it - User/PostController and Admin/PostController.
So when I wish to consume the resource, my routes would look something like this:
/user/post/:id
/admin/post/:id
Is this correct according to the convention or am I doing it wrong?
It is recommended to use a separate model for its controller and vice versa. This will allow you to maintain consistency and readability in the application, keeping the application easy to development.
In the application where there are admin and user accounts, I suggest using Middleware + (roles + permissions) and do not stand out in routing /user/ and /admin/ a apply general /user/
In the my example I described, you can use one method in the PostController, for example: show, add permission (eg user-post-list, admin), add roles (e.g. admin, user), in the routing describe who has permission (eg user-post-list, admin) to given function (show) and assign: permissions to role, roles to user - one or many role to user. You can also add roles to the routing and assigning the appropriate permissions in controller.
You can load permissions from the file using fixtures, for example permissions.csv, roles.csv, role_permission.csv or use seeds and fixtures or only seeds - before make create roles and permissions
- create the appropriate migrations)
Second solution (and only using Seed) it not flexible, because enlarging the list of permissions and roles involves modifying the code as opposed to the first solution - loading them from the file.
Of course, there are other solutions. I think that the proposing by me has a simple logic, it is effective and flexible to changes.
Check this: https://itsolutionstuff.com/post/laravel-56-user-roles-and-permissions-acl-using-spatie-tutorialexample.html
or
https://www.google.pl/search?q=laravel+roles+and+permissions+tutorial&oq=laravel+roles+&aqs=chrome.4.69i57j69i60j69i65j0l3.11595j0j7&sourceid=chrome&ie=UTF-8
Here is how I go about this problem in Laravel, when having users with different access-types.
Lets say we have a model like you, called Post. Now what we will do is, add a scope to that model, which we'll define a bit further down here:
use App\Scopes\AdminScope;
class Post extends Model {
// Apply a global scope to this controller
protected static function boot(){
parent::boot();
static::addGlobalScope(new AdminScope);
}
In the router you define it as a regular resource route:
Route::resource('posts', 'PostsController');
In the Controller, you can fetch all Posts on the index method like normal. This will after we create our admin scope, return all the posts in the system for admin users, and those belonging to a specific user for regular users:
class PostsController extends Controller {
public function index(){
$posts = Post::all();
}
No comes the part where you differentiate wether returning all the posts in the system, or just the ones belonging to the current user, based on the user type that is logged in:
Create a new folder in your app-folder named Scopes. In this folder, create a new file called AdminScope.php which will look something like this:
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Auth;
class AdminScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
// IF THE CURRENT USER TYPE IS NOT ADMIN, ALTER THE QUERIES:
if( Auth::user()->type != "admin" ){
$builder->where('user_id', '=', Auth::user()->id)
}
}
}
Ofcourse, this last file you will need to alter to meet the requirements on how you differentiate between a normal user and an administrator.
The good thing about this approach, is that you now can apply this Scope to any model where you see it fit, and it will alter all the queries for non-admin users to only show the Models that they own where the scope is applied.
Note:
This is a global scope, that will apply to all the Eloquent Models where it has been added, and to all queries made for that model. If you want, you can also write conditional local scopes, which you can read more about here:
Local Scopes

Laravel Structure - dependency injecting a controller

I have a laravel app and have created a SocialMediaController to get the latest posts from twitter and instagram and store them in the database.
I need them to be universally accessible and know I can access it via the IOC.
public function doSomething(App\Http\Controllers\SocialMediaController
$SocialMedia) {
}
However if feels wrong to inject a controller like this. What is be best way to wrap up these methods for global use?
It seems like you want to share some logic you have in the SocialMediaController with another controller, right?
Instead of trying to pass a controller instance to the controller action using the service container, you may move the current logic to a service. Refer to Service Container and Service Providers in the Laravel docs on how to create your own services.
Another way to achieve that is using a trait. Check this answer on how you can do that https://stackoverflow.com/a/30365349/1128918. You would end up with something like this:
trait SocialFeeder {
public function getLatestFromTwitter() {
...
}
}
And, then, you can use that new trait with your controllers:
class SocialMediaController extends Controller {
use SocialFeeder;
public function doSomething() {
...
$tweets = $this->getLatestFromTwitter();
...
}
}

Laravel setting from database and accessible to all the Controllers and Views

Hi I am trying to load the Setting and Accessible to all the Controllers and views. Is there a nice way to do this.
As I mentioned, you can modify the controller that all other controllers extend from, and add the variables to the __construct() function. For instance in my 5.3 project, all controllers extend App\Http\Controllers\Controller. In that controller, add (or update, if the function exists) something like this:
protected $settings; // Protected so child controllers can't modify it
public function __construct() {
$this->settings = Settings::first();
}
You can do all sorts of logic here and set up any variables you want. Then in the child controllers, all you have to do to access them is use $this->settings, like
if($this->settings->my_setting == TRUE)
If you want to use it in a view, you'll have to pass it into the view. If you want to share it will all views, you can follow the instructions here.

The preferred approach to pass variables to blade view

So I'm working on a small CMS in Laravel 5, and the one of the first things that I don't fully get is passing variables to a view.
I have seen things like
return View('view', array('name' => 'your name here'));
Or variables in a view composer
public function compose($view) {
$view->with(Config::get('configfile'));
}
but I'm still wondering if there is a better/more elegant way to do this. For example, with the first approach, I have to give it that array at every view, which just seems like a hassle, and with the view composers, it just feels like there should be a better solution.
Is there a recommended way to push these variables on the the view?
Also, I'm talking about a set of variables that are needed in every view. for example the name and the slogan of the website.
Thanks.
Have you considered view()->share see the docs Sharing Data with All Views
<?php
namespace App\Providers;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
view()->share('key', 'value');
}
}
it allows you to share a piece to data with all your views. I don't see any reason why you couldn't load all your config in this way if your needing to access it on the majority of views.
You can use Route Model Binding: https://laravel.com/docs/5.2/routing#route-model-binding for model specific data binding. Other than that, there's no (practical) way to do it.

How to load Model into a controller in MVC

I am working on building a lightweight MVC, mainly for the learning process but I would like it to be good enough to use eventually.
Below is a basic example/demo of how a basic controller might would look, let's assume the URI has been processed and routed to this controller and these 2 methods.
1) I need to get data from database/cache/etc... inside my Model classes, I just need help on how I should load my models into my example controller below, you can see that I have added this below $profileData = $this->model->getProfile($userId) that is just made up and does not exist's, how could I get something like that to work though? Or should I load the model into the class a different way?
2) A lot of pages will require a user to be logged into the site. SHould I process that part below in the controller to check if a user is logged in, example, before building the profile page, check if user is logged in, if not then build a login page instead and add these checks inside of each controller method/page?
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
//GET data from a Model
$profileData = $this->model->getProfile($userId);
$this->view->load('userProfile', $profileData);
}
// domain.com/user/friends/
function friends()
{
//GET data from a Model
$friendsData = $this->model->getFriendlist();
$this->view->load('userFriends', $friendsData);
}
}
core
abstract class Core_Controller {
protected $view;
protected $model;
function __construct(DependencyContainer $dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
}
There are probably tons of ways to accomplish what you are trying.
The "easiest" is probably to just override the constructor and instantiate the model directly.
in User_Controller:
public function __construct(DependencyContainer $dc) {
parent::__construct($dc);
$this->model = new User_Model();
}
I'm guessing that you are looking for something a little more automated though. If you want the Model to have the same name as the controller minus "_Controller", just use get_class($this) in the constructor and use PHP's string functions to parse out what you want. Once you have that in a variable, you can use that variable to instantiate the model:
in Core_Controller:
public function __construct(DependencyContainer $dc) {
$this->view = new Core_View();
// $model_class should be 'User_Model' now
$model_class = str_replace('_Controller', '_Model', get_class($this));
// now instantiate the model
$this->model = new $model_class();
}
I haven't actually worked with any framework that can only have one model associated with each controller (except may CakePHP? I can't remember). With Symfony, the models and controllers are completely decoupled so you can use any model with any controller. You just instantiate the model as need. Symfony use the Doctrine ORM so for example, in a controller action, if you needed a model you would do something like this:
$model = Doctrine::getTable('User');
It might be worthwhile to consider a design more like that in order to promote a decoupled design and I promise that you will want more than one model in some controller at some point.
2.) As far as authentication. Something that seems to be fairly common is to have some sort of setting (whether in a config file or a member variable) that says whether or not the current action needs the user to be authenticated. This is processed each time the action runs (Yii calls these kinds of things filters). If the user needs to be logged in, it stores the page that they are trying to access, and then redirects them to a log in page (you should only ever have to create one). Once they properly authenticate, it will redirect them back to where they were originally heading.

Categories