Laravel - Dynamic APP_NAME config variable based on domain - php

How do I overwrite the APP_NAME config variable dynamically based on the domain/hostname so that I can reference it in all Controllers and Blade templates?
OR how can I create a global variable (single record from a Model) that changes dynamically based on the domain, so that it is accessible in all Controllers and Blade templates?
I have 3 domains - each for a different athletic conference/League - that share 100% the same code. Currently, I have a function in /app/Helper/Helper.php that I call from every Controller. This gets the correct League based on the domain, and allows me to send the correct set of data to each view.
// Get league info based on domain
public static function getLeague(Request $request)
{
$host = $request->getHost();
if (App::environment('production')) {
// Some fancy Substring logic to get the domain name
} else {
// Manually set $host to one of my domains for offline debugging
}
$league = League::where(['url' => $host])->first();
return $league;
}
The above works great for Views and Controllers I have built.
However, I also leverage several out-of-the-box views and email templates for User Registration, Login, Forgot Password, etc., that use APP_NAME.
Some of them (like the Login or Register screens) have a typical Controller so I can call my Helper function and just pass the League to the view. But for others, like email templates, I've tried going down the rabbit trail of Controllers and Functions that eventually produce the email content, and I cannot find where to call my Helper function and pass the League Name to the view.
An example of an email template using APP_NAME, that I cannot figure out how to pass $league to it instead of it using the config variable, is:
/vendor/laravel/framework/src/Illuminate/Mail/resources/views/html/message.blade.php
So I'm stuck trying to figure out how to set the APP_NAME (or automatically calling my Helper function and creating a global $league variable that I can use in Controllers and Blade templates).
I have tried updating 'name' in /config/app.php, but it errors when trying to call the Helper function.
<?php
use App\Helper\Helper;
//dd($_SERVER['SERVER_NAME']);
$league = $league = Helper::getLeagueWithHost($_SERVER['SERVER_NAME']);
return [
'name' => $league->name,
....
]
Error:
Fatal error: Uncaught RuntimeException: A facade root has not been set. in C:\...\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php on line 258
This all wouldn't be a problem to set manually in each .env file, but I have an AWS CodePipeline that deploys to each domain upon GitHub push, so I'd have to update 3 .env files every time.
Thanks for any help!

I found a way to accomplish the ultimate goal, but not in the manner being requested in my initial question.
As part of the AWS CodePipeline deployment, there are "Appspec" events that fire at various times in the process (application-stop, before-install, after-install, application-start, and validate-service).
I wrote a Bash script in the "after-install" event that checks the IP of the hostname (returns the Private IP of the current Lightsail instance being deployed). And depending on the hostname IP, it changes line 2 of my .env file to the desired APP_NAME.
There is some hard-coding involved, mapping each Lightsail Private IP to an APP_NAME (meh), but as long as I do not delete and recreate a Lightsail instance, the Private IPs will remain static. The only way to avoid that would be to solve the initial question of updating the APP_NAME at runtime based on the URL/domain.
So, if someone else is trying to solve this problem at runtime when a page loads, this solution will not work for you.

Related

Access a variable from an non associated Controller in Cakephp

I am developing a quiz using a 'form' in cakephp. I have declared a variable within my CourseModules controller ($passMark) where the HR developing the quiz can set the pass percentage the user needs to successfully complete the quiz. I have declared the variable like so:
case "Quiz":
$quiz = $this->CourseModules->FormTemplates->find('list')->where(['active'=>true,'type'=>'Quiz']);
$passMark = [100=>'100%',90=>'90%',80=>'80%',70=>'70%',60=>'60%',
50=>'50%',40=>'40%',30=>'30%',20=>'20%',10=>'10%',0=>'0%'];
$this->set('passMark',$passMark);
$this->set('quiz',$quiz);
break;
I then need to access the variable $passMark within my FormsController so that I can check it against another variable ($percCorrect). $percCorrect is declared as so in my Forms Controller:
$percCorrect = $numberCorrect / $numberOfQuizQuestions * 100;
$this->set('percCorrect', $percCorrect);
I want to do an if statement so check in if $percCorrect is < $passMark but I'm unsure how to access $passMark because CourseModules isn't associated with Forms Controller.
I do have another controller called CoursesEnrolledModules that is related so I'm wondering if I can somehow access it through there?
I have the following code in my FormsController to load the CoursesEnrolledModules:
//Check if courses_enrolled_module_id is set
$courses_enrolled_module_id = $this->request->getQuery('courses_enrolled_module_id');
//If so make sure it is valid
if($courses_enrolled_module_id){
$this->loadModel('CoursesEnrolledModules');
$coursesEnrolledModule = $this->CoursesEnrolledModules->get($courses_enrolled_module_id,
['contain'=>[],
]);
//Pass variable to view so we can show correct back button
$this->set('coursesEnrolledModule', $coursesEnrolledModule);
//Also after save we will redirect.
}
Any time you ask yourself "how do I access one controller from another controller", just stop yourself right there and think about a different option. In this case, you should put your array in a central location accessible by everything that might need it. Include it in the config in your app.php, perhaps, and then reference it with Configure::read(...) anywhere you need it.

Laravel - storing into session from service provider?

I am using GeoIP package to get the user's IP and translate it into a zipcode. I don't want to do that for every request that the user is making but rather do a one time IP to zipcode, store it into session and then when I need to use it just check if the zipcode exists inside the session.
I tried to place the code inside AppServiceProvider#boot but it does not work. It is not remembered into the session. I tried inside routes but not working as well.
edit
The code inside boot method of appserviceprovider. This is just a test.
If (! Session()->has ('zipcode'))
Session(['zipcode' => geocodeZipcode()]);
The problem is that this runs everytime since the zipcode is not persisted in the session. The if is never false from my tests so far.
Where do I need to put the code to store the zipcode into the session and have it remembered even if the user is not logged in?
I basically need something like this:
1- User accesses a page on the server for the first time (any page)
2- I get the user IP and translate it to a zipcode
3- I store the zipcode into the session
4- For every other request the user makes I check if the zipcode exists into the session. If not I execute step 2.
5- Use the zipcode for its purpose
Where should I place the step 2 and 3?
In Laravel the session is initialized via middleware, and all the middlewares execute after the service providers boot phase
This is the reason why in your service provider you can't access the session: it has not been initialized yet
You should place your steps 2 and 3 in a middleware:
class ZipCodeMiddleware
{
public function handle( Request $request, Closure $next )
{
//ZIP CODE NOT FOUND IN SESSION: CREATE IT AND STORE
if ( ! Session::has( 'zipcode' ) )
{
//get ip and translate to zip
//store zip in the session
}
//use zip code here or access it later from Session
return $next($request);
}
}
Once you've stored the zip code in the session, you can access it from a controllers directly from the session, or, you could instance a class in the middleware and re-access it later with:
//use zip code here or access it later from Session
$zipClass = new ZipClass( $zipCode );
App::instance( ZipClass::class, $zipClass );
This way you can auto-inject the ZipClass depencency in your controllers and Laravel will give you back the $zipClass instance you built previously in the middleware

Changing the behaviour of view in Codeigniter

I am using codeigniter for a project that is used by a variety of companies.
The default version of our software is up and running and works fine - however some of our customers want slightly different view files for their instance of the system.
Ideally what I would like to do is set a variable (for example VIEW_SUFFIX) and whenever a view file is loaded it would first check if there was a suffix version available if there was use that instead.
For example if the system had a standard view file called 'my_view.php' but one client had a VIEW_SUFFIX of 'client_1' - whenever I called $this->load->view('my_view') if the VIEW_SUFFIX was set it would first check if my_view_client_1 existed (and if it did use that) or if not use the default my_view.php.
I hope that my question is clear enough... If anyone has done this before or can think of a way to do it I would really appreciate it.
EDIT:
Ideally I would like a solution that works without me changing every place that I am calling the view files. Firstly because there are a few files that may want different client versions and also because the view files are called from a lot of controllers
I had a similar requirement for which I created a helper function. Among other things, this function can check for a suffix before loading the specified view file. This function can check for the suffix and check if the file exists before loading it.
Unfortunately, the file checking logic would be a bit brittle. As an alternative, you can implement a MY_Loader class that will override the basic CI_Loader class.
Something like this in your application/core/MY_Loader.php:
class MY_Loader extends CI_Loader {
protected function _ci_load($_ci_data)
{
// Copy paste code from CI with your modifications to check prefix.
}
}
Could you not do this
// some method of creating $client
// probably created at login
$_SESSION['client'] = 'client_1';
$client = (isset($_SESSION['client'])) ? $_SESSION['client'] : '';
$this->load->view("your_view{$client}", $data);

cakephp database logger - how to access controller variable?

I am following the cakephp documentation to implement the database logger function. I got everything working after creating the logger class in app/Lib/Log/Engine/DatabaseLogger.php
and add these lines to bootstrap
CakeLog::config('otherFile', array(
'engine' => 'DatabaseLogger',
'model' => 'LogEntry',
// ...
));
After this I can log to the database by calling
CakeLog::info(message);
Everything works fine, but then I ran into problem trying to automatically log the user's username and ip address. I have searched for many solutions onlien but I do not seems to be able to find an answer. Is it possible to access the controller in the DatabaseLogger class?
You don't need access to the controller
The information you need is effectively global, you don't need to access the controller object to get it.
Client IP
You can use the Router::getRequest method to get the current request object and retrieve the client IP from that:
$request = Router::getRequest();
$ip = $request->clientIp();
Or simply use the env method:
$ip = env('REMOTE_ADDR');
Current User's name
For this, just use the static session interface:
$name = CakeSession::read('Auth.User.name');
This will hopefully get you going in the right direction. I'm currently in the process of migrating some of my CakePHP apps up to 2.2. I'm not 100% certain on this, but it seems to be about the way to go about this. I am thinking about using database logging and I like your idea so let me know how this works out. If it doesn't, provide feedback and I'll dig deeper. ;)
Create your LogEntry model (database table) to include fields for the username and the ip address.
Create an AppController. In the beforeFilter method place the user's ip address in their session. The CakeRequest object will contain their IP address.
$this->request->clientIp();
In your beforeSave() method for that model, you will collect the username and IP address.
To access the username field (assuming you are using Auth component and clientIp as the key to their ip address):
App::import('Component', 'Session');
$Session = new SessionComponent();
$user = $Session->read('Auth.User');
$ip = $Session->read('clientIp');
Assign these to the right fields so that they will be saved.

Detect when CodeIgniter fails to connect to a Db

I've got a CI instance which connects to a Db and checks permissions before serving pages. If the page isn't accessible to the current user, it redirects to a login page.
The login page, obviously, has permissions set so that it's accessible to all users.
After a recent glitch, the database server came back up on a different IP address (thank you Amazon, EC2). This resulted in CI being unable to check permissions for any page - including Login. Since the code assumes anything other than a Yes is a No, it redirected to Login. The result was an infinite redirect loop.
While this exact problem shouldn't happen again (Static elastic IP), I'd like to detect when the Db connection is down and handle it appropriately.
I've seen This SO Question which is what I'm trying to achieve but I'm not explicitly loading the database in any controllers, it's in the autoload config file.
So,
How can I query the state of the Db connection from inside CI? Do I have to run a useless query and check if I get results back or is there a more elegant solution?
Edit: The check is currently being performed in a hook:
$hook['post_controller_constructor'] = array(
'class' => 'AuthHook',
'function' => 'ValidateCredentials',
'filename' => 'auth.php',
'filepath' => 'hooks'
);
You can extend the controller and load the database in its constructor:
class My_Controller extends CI_Controller {
public function __construct(){
parent::__construct();
if ( $this->load->database() === FALSE )
{
//do something
}
}
}
All your controllers will inherit the new controller.
"Since the code assumes anything other than a Yes is a No, it redirected to Login."
So therefore you only need to alter the login logic function to specifically check for a DB connection (and thus still auto-load the database).
Use something like
$result = $this->db->conn_id;

Categories