I want to detect subdomains in routes.php in my L4 website and want to store that subdomain value somewhere so that I can access that value in each controller.
How can I do that ?
Please help
http://laravel.com/docs/routing#sub-domain-routing
Route::group(array('domain' => '{account}.myapp.com'), function() {
Route::get('user/{id}', function($account, $id) {
//
});
});
You can put this type of method in routes. However, I think it's a better fit for a filter in the 'app/filters.php' file. Try this:
Route::filter('getSubdomain', function($route, $request)
{
$host = $request->getHost();
$parts = explode('.', $host);
$subdomain = $parts[0];
// Store subdomain in session
Session::put('subdomain', $subdomain);
});
Then add the filter to the route (probably a group route) as follows:
Route::group(array('before' => 'getSubdomain'), function()
{
... add route stuff here ..
});
You can read more about how to use Laravel filters here:
http://laravel.com/docs/routing#route-filters
You can use Request to get your domain anywhere.
So, create a BaseController and add a method to get the current domain on all your extended controllers:
class BaseController extends Controller {
public function getDomain()
{
return Request::getHost();
}
}
And use it:
class PostsController extends BaseController {
public function store()
{
$post = new Post;
$post->domain_id = Domain::where('name', $this->getDomain())->first()->id;
$post->save();
}
}
Of course, this controller example supposes that you have a Domain model:
class Domain extends Eloquent {
private $table = 'domains';
}
EDIT:
Unless you have a very good reason for it, you don't have to use routes or store your subdomain on a session, unless you have a really good reason for this, it's a smell. Take a look at Laravel's code, there only one session stored by it: Laravel's session.
You can create a helper function:
Create a app/helpers/functions.php file (this is just an example) and add this helper function there:
function getCurrentSubdomain()
{
$domain = Config::get('app.domain');
preg_match("/^(.*)(\.$domain)$/", Request::getHost(), $parts);
return $parts[1];
}
Open your app/config/app.php and add a domain configuration to it:
return array(
'domain' => 'myapp.com',
...
);
Add the file to the autoload section of your composer.json:
"autoload": {
"classmap": [
...
],
"files": [
"app/helpers/functions.php"
]
},
Then you can use it everywhere: controllers, classes, routers, etc. Here's the same example as before, using it:
class PostsController extends BaseController {
public function store()
{
$post = new Post;
$post->domain_id = Domain::where('name', getCurrentSubdomain())->first()->id;
$post->save();
}
}
You can also create a class and a Facade for it, so you can call this class from anywhere, like Laravel does:
Helper::getCurrentSubdomain();
Or you can do the same by just creating a class and create a static function (less testable).
This is working on my Laravel 4.2 right now.
On your routes file:
Route::group(['domain' => '{subdomain}.myapp.com'], function()
{
Route::get('/', function($subdomain)
{
return "Your subdomain is: ".$subdomain;
});
});
Get subdomain in your controllers or anywhere:
$subdomain = Route::getCurrentRoute()->getParameter('subdomain');
Related
I have two controllers. StudentController and TeacherController. I have a variable $chat which I want to pass in all the views of StudentController and TeacherController. The $chat will contain different data for both these controllers.
I searched and found ways, but I am getting empty data. I am doing it like this.
<?php
namespace App\Http\Controllers;
use View;
class StudentController extends Controller {
public function __construct()
{
$this->middleware('auth')->except(['home']);
$this->middleware('access')->except(['home']);
$chats = studentChat();
View::share('chats', $chats);
}
So, here I am printing and it is returning an empty array, but when I use the same in a function the array contains data. What is wrong here? Can anyone please help?
What I tried:
public function boot()
{
View::composer('*', function ($view) {
$chats = Cache::remember('chats', 60, function () {
if(Auth::user()->user_type() == config('constant.student'))
{
return studentChat();
}
else
{
return teacherChat();
}
});
$view->with('chats', $chats);
});
}
If you use View::share your share data to ALL your view, if you need to add data to few different views you may do this:
Create blade file(chat.blade.php for your case), and put your variables:
<? $chats = studentChat(); ?>
Include this file to the begining of your views where your need this 'global' varables:
//begin of your blade file
#include('chat')
//some code
{{ $chat->channel }}
Sharing Data With All Views
Occasionally, you may need to share a piece of data with all views that are rendered by your application. You may do so using the view facade's share method. Typically, you should place calls to share within a service provider's boot method. You are free to add them to the AppServiceProvider or generate a separate service provider to house them:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$chats = studentChat();
View::share('chats', $chats);
}
public function register()
{
//
}
}
So, what I did was in the AppServiceProvider class, in the boot function I added this.
View::composer('*', function ($view) {
if(!\Auth::check())
{
return;
}
$userType = \Auth::user()->user_type ;
if($userType == config('constant.student'))
{
$chats = studentChat();
}
else if($userType == config('constant.teacher'))
{
$chats = teacherChat();
}
$view->with('chats', $chats);
});
You can pass data to the view Like.
return View::make('demo')->with('posts', $posts);
For more details visit article : Introduction to Routing in Laravel
write your query in boot method in appServiceProvider like,
View::composer('*', function ($view) {
$share_query = Cache::remember('share_query', 60,function () {
return App\User::all();
});
$view->with('share_query', $share_query);
});
Your final solution is ok, but not the cleanest possible.
Here is what i would do.
Define a class with a single function that contains your logic and return $chats, that way you will encapsulate your logic properly and keep your service provider boot method clean.
Then you have 2 options:
Inject your class in the boot() method of the service provider you use, then call its function and uses View::share. Should looks like :
public function boot(ChatResolver $chatResolver)
{
$chats = $chatResolver->getChats();
View::share(compact('chats));
}
If you only use $chats variable in a signe view or partial (like a part of layout), you can also inject the class you defined directly in the view.
Here is a link to Laravel doc regarding that.
In some cases it might be the easiest solution.
In laravel i have defined a route like this
Route::get('/', array(){
'as' => 'index',
'uses' => 'HomeController#index'
});
The function index() in the HomeController contains
public function index(){
$index = new ExampleModel;
$data = $index->getExampleList();
return View::make('public.index');
}
Now the problem is i have a master layout called happypath inside layouts folder in my views which yields this public.index content and i need to pass this $data to layouts.happypath. How do i do this ?
You can use a view composer for example:
namespace App\Providers;
use App\ExampleModel;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
protected $exampleModel;
public function __construct(ExampleModel $exampleModel)
{
$this->exampleModel = $exampleModel;
}
public function boot()
{
view()->composer('layouts.happypath', function ($view) {
$view->with('publicIndex', $this->exampleModel->getExampleList());
});
}
public function register()
{
//
}
}
So, every time you use/render the layouts.happypath the $publicIndex variable will be attached within the layout. Also you need to add the ComposerServiceProvider class in your config/app.php file in the providers array. You may access/reference the data using $publicIndex variable in your layout. There are other ways like global shared $information using view()->share(...) method to share a peace of data all over the views but this may help you.
I could not figure out the ComposerServiceProvider View::composer thing. So i basically solved it like this in Laravel 4.2. Added this code to the BaseController.php
protected $menuList;
public function __construct() {
$response = API::pool([
['GET', API::url('level')],
]);
$index = new Index();
$index->setCourseList($response[0]->json()['Category']);
$result = $index->getCourseList();
View::share('result', $result); //This line shares the $result globally across all the views in laravel 4.2
}
This can be done with a Service Provider. You can either use an existing one or create a new one.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\ExampleModel;
class ViewServiceProvider extends ServiceProvider
{
public function boot()
{
$index = new ExampleModel;
$data = $index->getExampleList();
view()->share('public.index', $data);
}
public function register()
{
}
}
Source: EasyLaravel.com
How to get URI parameters in methods inside a implicit controller?
First, I define a base route:
Route::controller('users', 'UserController');
Then,
class UserController extends BaseController {
public function getIndex()
{
//
}
public function postProfile()
{
//
}
public function anyLogin()
{
//
}
}
If I want to pass aditional parameters in URI, like http://myapp/users/{param1}/{param2} , how can I read param1 and param2 inside the respectve method? In this example, getIndex()
If you want to have URL like http://myapp/users/{param1}/{param2}
you need to have in your controller like this:
Route::get('users/{param1}/{param2}', 'UserController#getIndex');
and access it:
class UserController extends BaseController {
public function getIndex($param1, $param2)
{
//
}
}
but hey, you can also do something like this, the routes will be same:
class UserController extends BaseController {
public function getIndex()
{
$param1 = Input::get('param1');
$param2 = Input::get('param2');
}
}
but your URL would be something like: http://myapp/users?param1=value¶m2=value
Here is a way of creating a directory like hierarchy between models (nested routing)
Assume Album has Images, we would like an album controller (to retrieve album data) and an image controller (to retrieve image data).
Route::group(array('before' => 'auth', 'prefix' => 'album/{album_id}'), function()
{
// urls can look like /album/145/image/* where * is implicit in image controller.
Route::controller('image', 'ImageController');
});
Route::group(array('before' => 'auth'), function()
{
// urls can look like /album/* where * is implicit in album controller.
Route::controller('album', 'AlbumController');
});
I have to convert a site to make use of wildcard subdomains (right now it isn't using them).
I like using the routing style of Route::controller('user', 'UserController');
The subdomain example in the docs is:
Route::group(array('domain' => '{account}.myapp.com'), function()
{
Route::get('user/{id}', function($account, $id)
{
//
});
});
But this uses the format of Route::get('user/{id}'.... The user controller does so much more, and the first method handles everything so I prefer it.
I'd like to get the subdomain information into the user controller (and all other controllers, really), in a "Laravel way" if at all possible. Is there a standard way of doing that?
Route::group(array('domain' => '{account}.myapp.com'), function()
{
Route::controller('user', 'UserController');
}
Route::get('/',array('as'=>'homePage' , function()
{
return "my home page";
}));
Then in UserController, add $account as the first parameter in your methods
class UserController extends BaseController {
public function getIndex($account)
{
return $account;
}
}
I'm doing what I want in a certain way and I'm looking for alternatives or better ways of doing.
I'm using Resource Controllers in my application. Also I'm using softdelete in several models, so my routes are as follows:
Route::get('users/deleted', array('uses' => 'UserController#trash'));
Route::put('users/{id}/restore', array('uses' => 'UserController#restore'));
Route::resource('users', 'UserController');
The first route is to display objects that have been deleted.
The second allows me to restore these deleted elements.
The third maps the traditional methods (create, edit, update, etc.).
I have several controllers that work exactly the same way, I wonder if have any way to tell laravel to work with this two methods (trash and delete) by default without the two extra lines.
Is it possible? Or to give a better way that I'm doing?
(sorry for bad english)
Keeping things simple and DRY.
You can extend the Router and replace the Route Facade in your app/config/app.php file but seems like a lot of work for not much gain, but don't forget that your routes file is a PHP script and you can do things like:
$routes = [
['users' => 'UserController'],
['posts' => 'PostController'],
];
foreach ($routes as $key => $controller)
{
Route::get("$key/deleted", array('uses' => "$controller#trash"));
Route::put("$key/{id}/restore", array('uses' => "$controller#restore"));
Route::resource($key, $controller);
}
Extending the router
To extend the router you need to create 3 classes:
The Router extended, where you'll add your new methods:
<?php namespace App\Routing;
class ExtendedRouter extends \Illuminate\Routing\Router {
protected $resourceDefaults = array(
'index',
'create',
'store',
'show',
'edit',
'update',
'destroy',
'deleted',
'restore',
);
protected function addResourceDeleted($name, $base, $controller)
{
$uri = $this->getResourceUri($name).'/deleted';
return $this->get($uri, $this->getResourceAction($name, $controller, 'deleted'));
}
protected function addResourceRestore($name, $base, $controller)
{
$uri = $this->getResourceUri($name).'/{resource}/restore';
return $this->get($uri, $this->getResourceAction($name, $controller, 'restore'));
}
}
A Service Provider, to boot your new router, using the same IoC identifier Laravel uses ('router'):
<?php namespace App\Routing;
use Illuminate\Support\ServiceProvider;
class ExtendedRouterServiceProvider extends ServiceProvider {
protected $defer = true;
public function register()
{
$this->app['router'] = $this->app->share(function() { return new ExtendedRouter($this->app); });
}
public function provides()
{
return array('router');
}
}
And a Facade, to replace Laravel's one
<?php namespace App\Facades;
use Illuminate\Support\Facades\Facade as IlluminateFacade;
class ExtendedRouteFacade extends IlluminateFacade {
public static function is($name)
{
return static::$app['router']->currentRouteNamed($name);
}
public static function uses($action)
{
return static::$app['router']->currentRouteUses($action);
}
protected static function getFacadeAccessor() { return 'router'; }
}
Then you need to add your Service Provider and Facade to your app/config/app.php file, commenting the Laravel original ones.
Im using route model bind.
I do this way.
<?php
namespace App\Http\Router;
use Illuminate\Support\Facades\Route;
use Illuminate\Routing\Router;
class CustomRouter extends Router
{
public function customResource($routeName, $controllerPath, $routeBindName = null, $routeBindModel = null) {
$routeBindName = $routeBindName ?? $routeName;
$routeBindNameTrashed = "{$routeBindName}_trashed";
if($routeBindModel && $routeBindName) {
Route::model($routeBindName, $routeBindModel);
Route::bind($routeBindNameTrashed, function($modelId) use ($routeBindModel) {
return app($routeBindModel)->newQuery()->onlyTrashed()->findOrFail($modelId);
});
}
Route::put("$routeName/{{$routeBindNameTrashed}}/restore", "{$controllerPath}#restore")->name("{$routeName}.restore");
Route::delete("$routeName/{{$routeBindNameTrashed}}/force", "{$controllerPath}#forceDelete")->name("{$routeName}.force-delete");
Route::resource($routeName, $controllerPath);
}
}
Alter bootstrap/app.php
$app->singleton(
'router',
App\Http\Router\CustomRouter::class
);
Then usage:
Route::customResource('projects', 'ProjectsController', 'project', \App\Models\Project::class);
Route:list
Works like a charm
$routes = [
'posts' => 'PostController',
'posts.comments' => 'PostCommentController',
];
foreach ($routes as $key => $controller)
{
Route::get("$key/deleted", array('uses' => "$controller#trash"));
Route::put("$key/{id}/restore", array('uses' => "$controller#restore"));
Route::resource($key, $controller);
}