In a Symfony3 project, which is using psr 4, I am storing configuration data by auto generating a php class in the 'var/cache/dev' folder.
Here is a simplified copy of the generated file:
<?php
use Project\Bundle\Metadata\AbstractMetadata;
class LocalizedMetadata extends AbstractMetadata
{
protected function populate ()
{
}
}
I know I can include or require the file, but the 'dev' directory changes between development and production, and I use the class in several places. Also, I'm hoping to use this bundle in other projects, so I don't want to hard code anything outside of the bundle. How are these files usually loaded? Is there anything within the Symfony core to assist?
Any classes created in the 'dev' directory cannot be auto loaded, at least not without configuring files outside of the bundle (eg. composer.yml).
I took a look into how Symfony manages it's router files, and the solution was actually quite simple.
I made a manager class which can be called from anywhere in the code. This class contains a method which returns an instance of the class in the cache folder.
public function getLocalizedRoutes ()
{
if ( null !== $this->localizedRoutes ) {
return $this->localizedRoutes;
}
$cache = $this->getConfigCacheFactory()->cache(
$this->options['cache_dir'].'/'.CachingMetadata::CACHE_FILE,
function (ConfigCacheInterface $cache) {
$content = CachingMetadata::getInstance()->dump();
$cache->write($content);
}
);
require_once $cache->getPath();
return $this->localizedRoutes = LocalizedMetadata::getInstance();
}
It uses Symfony's built in ConfigCache class, simply because it handles the file write and file permissions. Also, it will call the closure if the file doesn't exist. In this case the closure calls into the class which handles the loading of data and converting it into PHP code. The cache file is then loaded with require_once and an instance of LocalizedMetadata, which is the cached class name, is returned.
The above method can be modified to also pass resource files to ConfigCache, that way if one of your files is modified, the cache will be rewritten. In my case it wasn't required.
Making this work with a cache warmer simply requires a call to the above method:
class RouteCacheWarmer implements CacheWarmerInterface
{
private $_manager;
public function __construct ( LocaleManager $manager )
{
$this->_manager = $manager;
}
public function isOptional ()
{
return true;
}
public function warmUp ( $cacheDir )
{
$this->_manager->getLocalizedRoutes();
}
}
I would also like to note, it's possible to use namespaces within the cache file.
Related
I have two controller file homecontroller and backendcontroller. What is the best way to create global function and access it from both files?
I found here Arian Acosta's answer helpful but I wonder if there is an easiest way. I would appreciate any suggestions.
Solution
One way to do this is to create a class and use its instance, this way you can not only access the object of the class within a controller, blade, or any other class as well.
AppHelper file
In you app folder create a folder named Helpers and within it create a file name AppHelper or any of your choice
<?php
namespace App\Helpers;
class AppHelper
{
public function bladeHelper($someValue)
{
return "increment $someValue";
}
public function startQueryLog()
{
\DB::enableQueryLog();
}
public function showQueries()
{
dd(\DB::getQueryLog());
}
public static function instance()
{
return new AppHelper();
}
}
Usage
In a controller
When in a controller you can call the various functions
public function index()
{
//some code
//need to debug query
\App\Helpers\AppHelper::instance()->startQueryLog();
//some code that executes queries
\App\Helpers\AppHelper::instance()->showQueries();
}
In a blade file
Say you were in a blade file, here is how you can call the app blade helper function
some html code
{{ \App\Helpers\AppHelper::instance()->bladeHelper($value) }}
and then some html code
Reduce the overhead of namespace (Optional)
You can also reduce the overhead of call the complete function namespace \App\Helpers by creating alias for the AppHelper class in config\app.php
'aliases' => [
....
'AppHelper' => App\Helpers\AppHelper::class
]
and in your controller or your blade file, you can directly call
\AppHelper::instance()->functioName();
Easy Solution:
Create a new Helpers folder in your app directory.
Create a php file named your_helper_function.php in that Helpers directory.
Add your function(s) inside your_helper_function.php
function your_function($parameters){
//function logic
}
function your_another_function($parameters){
//function logic
}
Add this file to the Files key of your composer.json like
"autoload": {
...
"files": [
"app/Helpers/your_helper_function.php"
]
...
}
Finally, regenerate composer autoload files. (Run this in your project directory)
composer dump-autoload
That's it! and now you can access your_function() or your_another_function() in any part of your Laravel project.
If you still have any confusion, check my blog post on how to do this:
How to Add a Global Function in Laravel Using Composer?
Updated:
Step 1
Add folder inside app folder
app->Helper
Step 2
add php Class inside Helper folder
Eg. Helper.php
Add namespace and class to the Helper.php
namespace App\Helper;
class Helper
{
}
Register this Helper.php into config/app.php file
'aliases' => [
....
'Helper' => App\Helper\Helper::class
]
Now, write all the functions inside Helper.php and it will be accessible everywhere.
How to access from Controller?
Step 1 - Add a namespace at top of the controller.
use App\Helper\Helper;
Step 2 - Call function - Assume there a getInformation() inside the Helper Class.
$information = Helper::getInformation()
In your Controller.php which extends BaseController, you can create a function like;
public function data($arr = false)
{
$data['foo'] = 'bar';
return array_merge($data,$arr);
}
And from any controller when you send a data to a view;
public function example()
{
$data['smthg'] = 'smthgelse';
return view('myView',$this->data($data));
}
The data in the the main controller can be accessed from all controllers and blades.
The Laravel Service Provider way
I've been using global function within Laravel for a while and I want to share how I do it. It's kind of a mix between 2 answers in this post : https://stackoverflow.com/a/44021966/5543999 and https://stackoverflow.com/a/44024328/5543999
This way will load a file within a ServiceProvider and register it within your Laravel app.
Where is the difference, the scope, it's always about the scope.
Composer //Autload whitin composer.json method
|
|--->Laravel App //My method
|
|--->Controller //Trait method
|--->Blade //Trait method
|--->Listener //Trait method
|--->...
This is a really simplist way to explain my point, all three methods will achieve the purpose of the "Global function". The Traits method will need you to declare use App\Helpers\Trait; or App\Helpers\Trait::function().
The composer and service provider are almost about the same. For me, they answer better to the question of what is a global function, because they don't require to declare them on each place you want to use them. You just use them function(). The main difference is how you prefer things.
How to
Create the functions file : App\Functions\GlobalFunctions.php
//App\Functions\GlobalFunctions.php
<?php
function first_function()
{
//function logic
}
function second_function()
{
//function logic
}
Create a ServiceProvider:
//Into the console
php artisan make:provider GlobalFunctionsServiceProvider
Open the new file App\Providers\GlobalFunctionsServiceProvider.php and edit the register method
//App\Providers\GlobalFunctionsServiceProvider.php
public function register()
{
require_once base_path().'/app/Functions/GlobalFunctions.php';
}
Register your provider into App\Config\App.php wihtin the providers
//App\Config\App.php
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
...
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
App\Providers\GlobalFunctionsServiceProvider::class, //Add your service provider
Run some artisan's commands
//Into the console
php artisan clear-compiled
php artisan config:cache
Use your new global functions
//Use your function anywhere within your Laravel app
first_function();
second_function();
Laravel uses namespaces by default. So you need to follow the method described in that answer to setup a helper file.
Though in your case you want to access a method in different controllers. For this there's a simpler way. Add a method to you base controller app/Http/Controllers/Controller.php and you can access them in every other controller since they extend it.
// in app/Http/Controllers/Controller.php
protected function dummy()
{
return 'dummy';
}
// in homecontroller
$this->dummy();
There are a few ways, depending on the exact functionality you're trying to add.
1) Create a function inside Controller.php, and make all other controller extend that controller. You could somewhat compair this to the master.blade.php
2) Create a trait, a trait can do a lot for you, and keeping ur controllers clean. I personally love to use traits as it will look clean, keep my Controller.php from being a mess with tons of different lines of code.
Creating a global function
create a Helpers.php file under a folder, let's name it 'core'.
core
|
-- Helpers.php
namespace Helpers; // define Helper scope
if(!function_exists('html')) {
function html($string) {
// run some code
return $str;
}
}
In your composer.json
"autoload": {
"psr-4": {
},
"files": [
"core/Helpers.php"
]
}
in the file that you want to use it
// the " use " statement is not needed, core/Helpers is loaded on every page
if(condition_is_true) {
echo Helpers\html($string);die();
}
Remove the namespace in Helpers.php if you want to call your function without the need to prefix namespace. However I advise to leave it there.
Credit: https://dev.to/kingsconsult/how-to-create-laravel-8-helpers-function-global-function-d8n
By using composer.json and put the function containing file(globalhelper.php) to the autoload > files section, then run
composer dump-autoload
You can access the function inside the file(globalhelper.php) without having to calling the class name, just like using default php function.
here is my library file
libraray file name Commonlib
<?php
class Commonlib extends CI_Controller
{
public function __construct()
{
parent::__construct();
$ci=& get_instance();
$ci->load->database();
}
function getcountries()
{
return $ci->db->get("countries")->result();
}
}
in config file
$autoload['libraries'] = array('database','Commonlib');
here is my view
$a = new Commonlib();
$results=$a->getcountries();
foreach ($results as $row) {
// country_id
echo ''.$row->country .'<br>';
}
show this error Non-existent class:
where is trouble ? how to solve it ?
The problem is here of naming convention. Because Class names and file names must match.
<?php
class Commonlib {
public function __construct() {
parent::__construct();
$ci=& get_instance();
$ci->load->database();
}
function getcountries(){
return $ci->db->get("countries")->result();
}
}
In view
$a = new Commonlib();
$results=$a->getcountries();
foreach ($results as $row)
{ // country_id
echo ''.$row->country .'<br>';
}
Library name should be like this CI_Commonlib ... Please try.
go To your library and change it with this code.
<?php
class your_library_Name {
function getcountries(){
return $ci->db->get("countries")->result();
}
}
and this is how to call it
$rec= $this->your_library_Name->getcountries();
You can do any of a few things.
By default most people will just drop their library into system/libraries, and load it with either with the autoloader, or $this->load->library(). If you do this, you must prefix your class name with CI_ so that CodeIgniter will recognize it, since the system folder is intended for stock files. In your case, you would need:
class CI_Commonlib extends CI_Controller
{
}
This works, but it mixes your custom code into CodeIgniter system files, which can become very messy when you want to upgrade your installation.
My recommendation is to create a separate folder for your custom files, and then add that path to your loader object. You will not need to make any changes to the code if you move your Commonlib object to application/libraries/Commonlib.php.
People will often add custom objects to system when they want to share files between multiple CodeIgniter installations. Multiple applications can share the same BASEPATH, so it makes sense to put your code where the shared directory already exists.
The problem with this is the same issue with an upgrade path, where custom objects mix with stock code, and may get confusing. In this case defining your own shared folder can be very helpful. CodeIgniter lets you do that with $this->load->add_package_path() You can move your shared code into custom/libraries and be off to the races. In that case, you can update your controller (not view) accordingly.
https://www.codeigniter.com/user_guide/libraries/loader.html
$this->load->add_package_path('custom/libraries');
$this->load->library('commonlib');
So Laravel saves it's own session files when someone accesses the website in the /storage/framework/sessions folder. Each of these session file's names are a randomly generated alpha numeric unique name. But, I'd like to somehow rename the files and give my own custom name for it. I've got two options for that.
Change the file name manually once the session file is created (by a create, copy, replace)
Find the function which randomly generates the alphanumeric name and change it with my own way of setting a unique name to each file (this method might come with less complications)
My main end goal is to rename each user's session file to their own userid that's stored in my db. So the names are still unique, the only difference is that I can search through the files easier than if they had random alphanumeric names.
So if anyone knows how I could do any of the above methods or if you can think of a better way to achieve the same, it'd be great. Any help is greatly appreciated!
EDIT: Decided to update here with what I had decided to do finally. I decided not to use the built in session files generated by Laravel and realized it's much easier to make my own file and just have each client access it instead. Thanks to all!
Laravel has several Manager classes that manage the creation of
driver-based components. These include the cache, session,
authentication, and queue components. The manager class is responsible
for creating a particular driver implementation based on the
application's configuration. For example, the SessionManager class can
create File, Database, Cookie and various other implementations of
session drivers.
Each of these managers includes an extend method which may be used to
easily inject new driver resolution functionality into the manager.
To extending Laravel with a custom session driver, we will use the
extend method to register our custom code:
You should place your session extension code in the boot method of your AppServiceProvider.
Implement SessionHandlerInterface
app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Session;
use Illuminate\Support\ServiceProvider;
use App\Handlers\MyFileHandler;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Session::extend('file', function($app)
{
return new MyFileHandler();
});
}
}
Note that our custom session driver should implement the SessionHandlerInterface. This interface contains just a few simple methods we need to implement.
app/Handlers/MyFileHandler.php
<?php
namespace App\Handlers;
use SessionHandlerInterface;
class MyFileHandler implements SessionHandlerInterface {
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}
Or you can extend MyFileHandler from FileSessionHandler and override relevant methods.
Extend FileSessionHandler
app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Session;
use Illuminate\Support\ServiceProvider;
use Illuminate\Session\FileSessionHandler;
use App\Handlers\MyFileHandler;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Session::extend('file', function($app)
{
$path = $app['config']['session.files'];
return new MyFileHandler($app['files'], $path);
});
}
}
app/Handlers/MyFileHandler.php
<?php
namespace App\Handlers;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Session\FileSessionHandler;
class MyFileHandler extends FileSessionHandler
{
public function __construct(Filesystem $files, $path)
{
parent::__construct($files, $path);
}
}
You can find more in Session section of Extending the framework document.
https://laravel.com/docs/5.0/extending#session
If your final goal is searching on session file names; you don't need to change them.
You can save session file names in a database table ( or another file your choice). You can use this link to get file names.
One column -> store session file names
other columns -> store another informations that you want
In this way you can search and find faster files with using SQL.
use middleware for the request
\Illuminate\Session\Middleware\StartSession::class
Route::group(['middleware' => [\Illuminate\Session\Middleware\StartSession::class]], function () {
});
I would like to cache my pages, which could be done by using:
$this->output->cache(n)
As in the documentation, i could use a custom output method in my controller, named _output.
The problem is, when I create this _output method in my controller, it doesn't create any cache file, only displays the output. I looked into the Output core class, and as I see if it's not find any method with _output, it just echo the content.
They have this in the documentation, to use in my _output:
if ($this->output->cache_expiration > 0)
{
$this->output->_write_cache($output);
}
But cache_expiration is not accessible...
You have 2 ways of solving this,
you can edit the core files and make it the cache_expiration to
public instead of protected and mess with the core files (DIRTY WAY)
or you can extend the CI_Output class (BETTER)
Create a file name it MY_Output.php in your application/core the MY_ prefix is found on the $config['subclass_prefix'] option in your application/config/config.php file.,
then put this little piece of code:
class MY_Output Extends CI_Output{
public $the_public_cache_expiration_ = 0;
public function get_cache_expiration()
{
$this->cache_expiration_ = $this->cache_expiration;
}
}
What we are doing is we are extending the CI_Output class and adding our very own method, when the method get_cache_expiration() is called it will assign the cache_expiration to the_public_cache_expiration_, use it on your _output.
test it using this:
public function _output($output)
{
$this->output->get_cache_expiration();
echo '<pre>';
print_r($this->output->the_public_cache_expiration_);//get only our cache expiration
echo '<hr>';
print_r($this->output);//print the whole Output class
}
benefits:
We did no mess with the core files.
we can add more functionality later to our child class.
here is the basic _output method in use
public function _output($output)
{
$this->output->get_cache_expiration();
if($this->output->cache_expiration_ > 0)
{
$this->output->_write_cache($output);
}
echo $output;
}
I am bootstrapping my application with Zend_Application_Module_Bootstrap inside the various module directories. How can I require that a resource inside another module's bootstrap be executed first?
// app/modules/user/Bootstrap.php
class User_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initUser()
{
}
}
// app/modules/author/Bootstrap.php
class Author_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initAuthor()
{
$this->bootstrap('user'); // Fatal: Resource matching 'user' not found
}
}
I've decided to use plugins to achieve this fine-grained functionality, as execution order cannot be managed properly, therefore making module bootstraps a poor choice to place code with dependencies.
Used referenced answer below to base my decision:
load /execute module based bootstraps from each module in certain order
According to this thread from the ZF1 mailing list, you can access the module-bootstraps via the modules resource of the application bootstrap.
Man, what a mouthful. Here's what I mean:
// app/modules/user/Bootstrap.php
class User_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initUser()
{
}
}
// app/modules/author/Bootstrap.php
class Author_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initAuthor()
{
$app = $this->getApplication(); // it's actually the application *bootstrap*
$app->bootstrap('modules');
$modulesResource = $app->getResource('modules');
$userBootstrap = $modulesResource->user;
$userBootstrap->bootstrap('user'); // should be cool
}
}
In my own experience, as soon as one of my module-level resources needs to be referenced in more than one module - especially during bootstrapping - I just push the bootstrapping of that resource up into the application-level bootstrap.