I am referring to this documentation on how to use sessions with Zend Framework:
http://framework.zend.com/manual/1.12/en/learning.multiuser.sessions.html
I have started with a skeletal application at this point the only thing I have modified is the controller. I am posting the code for Application module controller:
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* #link http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
* #copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* #license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function indexAction()
{
$mysession = new Zend_Session_Namespace('mysession');
if (!isset($mysession->counter)) {
$mysession->counter = 1000;
} else {
$mysession->counter++;
}
if ($mysession->counter > 1999) {
unset($mysession->counter);
}
return new ViewModel();
}
}
The first thing I see when I go to the route is:
Fatal error: Class 'Application\Controller\Zend_Session_Namespace' not found in C:\Program Files (x86)\Zend\Apache2\htdocs\zf2-sessioncounter\module\Application\src\Application\Controller\IndexController.php on line 19
So I am thinking this has to do with a use Zend_Session_Namespace at this point or perhaps it is because my application is not set up for Zend_Application? Would this be a correct place to put the session php?
Zend_Session_Namespace is actually a component of ZF1 - which shouldn't be used in ZF2 application because of many reasons (NOT using proper namespace mechanism being the most important).
Try Zend\Session\Container instead:
use Zend\Session\Container;
// ...
$mysession = new Container('mysession');
Related
I have installed the Laravel in sub-folder and is trying to install the horizon. After routing to "test.com/sub-folder/horizon", all the design in broken and also the internal links are pointing to main domain instead of main-domain-without-subfolder.
After the search, it seems to be the known issue which is already reported in github issue
Has there is any work around to make horizon work when Laravel is installed in sub-folder?
I have a solution that only involves PHP.
The issue, as pointed out by #Isaiahiroko, is the basePath defined for Horizon's interface. That code is in Laravel\Horizon\Http\Controllers\HomeController::index(). The idea is this: we are going to pass to Laravel's service container our own implementation of that controller that will override the basePath definition passed to Horizon's interface.
Create a new controller with code like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\Http\Controllers\HomeController;
class HorizonHomeController extends HomeController
{
/**
* Overrides default horizon route to support subdirectory hosting.
*/
public function index ()
{
// We use a plain request to check for the base url.
$request = request();
// Set up our base path.
$base_path = Str::substr($request->getBasePath(), 1);
if (!empty($base_path)) {
$base_path .= '/';
}
// Patch default horizon variables with our own base path.
$variables = Horizon::scriptVariables();
$variables['path'] = $base_path . config('horizon.path');
// Render horizon's home view.
return view('horizon::layout', [
'assetsAreCurrent' => Horizon::assetsAreCurrent(),
'horizonScriptVariables' => $variables,
'cssFile' => Horizon::$useDarkTheme ? 'app-dark.css' : 'app.css',
'isDownForMaintenance' => App::isDownForMaintenance(),
]);
}
}
What's left is telling Laravel's service container that when Horizon's HomeController is requested, it should provide our HorizonHomeController class. In your AppServiceProvider, at the end of the register() method, set this up:
// [...]
class AppServiceProvider extends ServiceProvider
{
// [...]
/**
* Register any application services.
*
* #return void
* #throws InvalidConfiguration
*/
public function register()
{
// [...]
// Horizon's subdirectory hack
$this->app->bind(
Laravel\Horizon\Http\Controllers\HomeController::class,
App\Http\Controllers\HorizonHomeController::class
);
}
// [...]
}
After that, you should be able to browse to http(s)://<your-host>/<your-sub-dir>/horizon normally.
Considerations:
To me this feels cleaner that patching a compiled js, which also has the downside that needs to be re-applied every time Horizon is updated (this can be mitigated with a post-update script in composer, tho). Also, for additional points, this solution is only overriding the method that renders the view, but not the route, which means all of Horizon's authentication mechanisms (middlewares and gates) are working exactly as described in the documentation.
If you desperately need to do this, here is a hack:
In public\vendor\horizon\app.js, search for window.Horizon.basePath
replace window.Horizon.basePath="/"+window.Horizon.path; with window.Horizon.basePath="/[you sub-directoy]/"+window.Horizon.path;
It should work...until you run update one day and it mysteriously stop working.
I'm using Laravel 5.5. The nature of the website is a 'multisite' architecture where multiple websites/domains are run from the same codebase.
I've come across an issue when sending email. I need to change the from name and address as well as the transport (SMTP, etc) options depending on which website is being viewed. I have these details stored in a config file.
The easiest way is to just pull those details in the Controller before I call Mail::send/Mail::queue and to update them. However, this brings back 2 issues:
There is a heavy reliance on remembering to actually do that every time I send any email in the code. In short, it's not abiding by DRY.
I'd be forced to use Mail::send instead of Mail::queue, because the queue wouldn't have any idea of the config update from the time it was queued only from when it is processed .
How can I achieve what I am looking to do here in a clean way?
I thought about extending all of my 'Mailable' classes with a custom class that updates the SMTP details, but it doesn't look like you can update the SMTP/Transport information after the class is initiated; you can only update the from name and address.
I managed to find a way to do this.
I had my mailable class (ContactFormMailable) extend a custom class, as follows:
<?php
namespace CustomGlobal\Mail;
use CustomGlobal\Mail\CustomMailable;
use CustomGlobal\ContactForm;
class ContactFormMailable extends CustomMailable
{
public $contact_form;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct(ContactForm $contact_form)
{
$this->contact_form = $contact_form;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$view = $this->get_custom_mail_view('contact_form', $this->contact_form);
return $this->subject('Contact Form Enquiry')
->view($view);
}
}
You'll notice I'm calling get_custom_mail_view. This is in my extended class and used to calculate the view and template I need to use for my mail, depending on the website being viewed. In here I also set the location of my config folder.
<?php
namespace CustomGlobal\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use Swift_Mailer;
use Swift_SmtpTransport;
use CustomGlobal\Website;
use CustomGlobal\Territory;
class CustomMailable extends Mailable
{
use Queueable, SerializesModels;
public $layout_view_to_serve;
public $host_folder;
/**
* Override Mailable functionality to support per-user mail settings
*
* #param \Illuminate\Contracts\Mail\Mailer $mailer
* #return void
*/
public function send(Mailer $mailer)
{
app()->call([$this, 'build']);
$config = config($this->host_folder .'.mail');
// Set SMTP details for this host
$host = $config['host'];
$port = $config['port'];
$encryption = $config['encryption'];
$transport = new Swift_SmtpTransport( $host, $port, $encryption );
$transport->setUsername($config['username']);
$transport->setPassword($config['password']);
$mailer->setSwiftMailer(new Swift_Mailer($transport));
$mailer->send($this->buildView(), $this->buildViewData(), function ($message) use($config) {
$message->from([$config['from']['address'] => $config['from']['name']]);
$this->buildFrom($message)
->buildRecipients($message)
->buildSubject($message)
->buildAttachments($message)
->runCallbacks($message);
});
}
/**
* Calculate the template we need to serve.
* $entity can be any object but it must contain a
* $website_id and $territory_id, as that is used
* to calculate the path.
*/
public function get_custom_mail_view($view_filename, $entity)
{
if(empty($view_filename)) {
throw new Exception('The get_custom_mail_view method requires a view to be passed as parameter 1.');
}
if(empty($entity->website_id) || empty($entity->territory_id)) {
throw new Exception('The get_custom_mail_view method must be passed an object containing a website_id and territory_id value.');
}
// Get the website and territory
$website = Website::findOrFail($entity->website_id);
$territory = Territory::findOrFail($entity->territory_id);
$view_to_serve = false;
$layout_view_to_serve = false;
// Be sure to replace . with _, as Laravel doesn't play nice with dots in folder names
$host_folder = str_replace('.', '_', $website->website_domain);
$this->host_folder = $host_folder; // Used for mail config later
/***
Truncated for readability. What's in this area isn't really important to this answer.
***/
$this->layout_view_to_serve = $layout_view_to_serve;
return $view_to_serve;
}
}
It's important to remember that mail can be queued. If you do this is another way, such as setting a config at runtime, then you'll find that the process that runs the queue has no visibility/scope of your runtime config changes, and you'll end up firing out email from your default values.
I found a few answers similar to this one, which helped me out, but none of them worked completely, and some are out-dated (Swift_SmtpTransport is changed considerably since those answers).
Hopefully this helps someone else out.
I want to implement some extra features to Illuminate\Translate\Translator.
So, i create my folder in ~/vendor directory, place there My/Traslator class, that will implement Symfony\Component\Translation\TranslatorInterface. Right?
Is it OK to extend laravel translator class (a lot of functionality will be duplicated otherwise) in my package?
If it is ok - it will be necessary to tie to current laravel version to keep code stable. But what will happen in case enduser laravel version will differ from one required in my package?
What should i do then to make laravel use my translator class in application (facades,etc)?
Make a Translator class and make it extend Illuminate\Translation\Translator
<?php
namespace App\Helpers;
use Illuminate\Translation\Translator as LaravelTranslator;
class Translator extends LaravelTranslator
{
// here you can overwrite any functions you want/need
}
Create your own TranslationServiceProvider inside app/providers (just copy the laravel translation service provider and change the line where it uses Translator with your own Translator class where you have overwritten what you needed)
<?php
namespace App\Providers;
use App\Helpers\Translator; // <= Your own class
use Illuminate\Translation\FileLoader;
use Illuminate\Support\ServiceProvider;
class TranslationServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerLoader();
$this->app->singleton('translator', function ($app) {
$loader = $app['translation.loader'];
// When registering the translator component, we'll need to set the default
// locale as well as the fallback locale. So, we'll grab the application
// configuration so we can easily get both of these values from there.
$locale = $app['config']['app.locale'];
$trans = new Translator($loader, $locale);
$trans->setFallback($app['config']['app.fallback_locale']);
return $trans;
});
}
/**
* Register the translation line loader.
*
* #return void
*/
protected function registerLoader()
{
$this->app->singleton('translation.loader', function ($app) {
return new FileLoader($app['files'], $app['path.lang']);
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return ['translator', 'translation.loader'];
}
}
Comment out or delete the Laravels translator service line inside config/app.php:
//Illuminate\Translation\TranslationServiceProvider::class,
Add your own Provider in that same array
App\Providers\TranslationServiceProvider::class,
This page has more information: http://laravel.com/docs/5.0/extending#container-based-extension
So what you need to do is:
Extend the built-in class from the vendor directory
Create a new service provider that add your translation class to the service container
Replace Laravel’s translation service provider in your config/app.php file with the namespace of your translation service provider
Now when you ask for the translation service provider out of the service container—either directly (app('translator')) or with the Lang façade, it will return your translation class rather than Laravel’s.
I'm using composer to install my package and I'm trying to install my bundle in a very specific location.
In short, I have my packages with the following namespace prefixes:
TeamXyz/AbcBundle
TeamXyz/AbcComponent
Now, since we have tons of components and bundles, we want to separate them into 2 folders like this:
vendor/TeamXyz/Component/AbcComponent
vendor/TeamXyz/Bundle/AbcBundle
It used to work very well but with PSR-4 I cannot seem to be able to control the location where my package should be installed. I wonder if I have to write custom package installer for composer to be able to do this?
Ok, I found out a way to do that, simply wrote my own custom package installer and voila.
<?php
/**
* Created by myTeam.
* Date: 6/11/14
* Time: 1:43 PM
* Question? Come to our website at http://my.com
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace My\PackageInstaller;
use Composer\Package\PackageInterface;
use Composer\Installer\LibraryInstaller;
class BundleInstaller extends LibraryInstaller
{
/**
* {#inheritDoc}
*/
public function getPackageBasePath(PackageInterface $package)
{
$prefix = substr($package->getPrettyName(), 0, 3);
if ('my/' !== $prefix) {
throw new \InvalidArgumentException(
'Unable to install package, my package '
. 'should always start their package name with '
. '"my/"'
);
}
$packageName = str_replace('my/', '', $package->getPrettyName());
return $this->vendorDir . '/my/Bundle/' . $packageName;
}
/**
* {#inheritDoc}
*/
public function supports($packageType)
{
return 'my-bundle' === $packageType;
}
}
And yes I understand everything you guys complain about, but a tool is a tool and one person can use it the best way that fits his need. At least that's what I believe :). Maybe I'm doing something stupid here for not using Composer the exact way it was meant to be used. But hey, if we are using all the tools the exact ways they were meant to be used, lots of innovation would have happened in the first place :)
After the last two comments, I'll dump out my real code and maybe it will help out:
Here is the landing Controller:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Businessbuilder extends CI_Controller {
function __construct()
{
parent::__construct();
}
function index()
{
$RTR = $GLOBALS["RTR"];
// import the necessary libraries
$this->load->model("site_pages");
$RTR = $GLOBALS["RTR"];
// get the current site
$site = current_site();
// get the requesting url
$class = $RTR->uri->rsegments[1];
$function = $RTR->uri->rsegments[2];
// get the current function and class
$current_method = explode("::", __METHOD__);
// get the real class name that is going to be called
$site_page = $this->site_pages->get(array("display_name"=>$class, "id"=>$site->id));
$site_page = $site_page->result();
if(count($site_page) == 1)
{
$site_page = $site_page[0];
// set the class name to be called
$class = $site_page->class_name;
}
// only execute if the requested url is not the current url
if(!(strtolower($class) == strtolower($current_method[0]) && strtolower($function) == strtolower($current_method[1])))
{
if(!file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$class.EXT))
{
show_404($RTR->fetch_directory().$class);
exit;
}
// include the required file. I use require once incase it is a file that I've already included
require_once(APPPATH.'controllers/'.$RTR->fetch_directory().$class.EXT);
// create an instance of the class
$CI = new $class();
if(method_exists($CI, $function))
// call the method
call_user_func_array(array(&$CI, $function), array_slice($RTR->uri->rsegments, 2));
else
{
show_404($RTR->fetch_directory().$class);
exit;
}
}
}
}
here is an example of a dynamic controller that will be called:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Public_homepage extends CI_Controller {
function __construct()
{
parent::__construct();
}
function index()
{
echo "<br /><br /><br />";
$this->load->model("sites");
$style = $this->sites->get(array("id"=>1)); // fail here, sites not defined
//print_r($style);
exit;
$view_params = array();
$view_params["site_id"] = $this->site_id;
$this->load->view('public_homepage', $view_params);
}
}
Here is my model that I am using:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Sites extends CI_Model
{
function __construct()
{
parent::__construct();
}
function get($search = array())
{
return $this->db->query("SELECT * FROM sites"); // failure on this line, db undefined
}
}
the error that I am getting is either this (error1):
A PHP Error was encountered
Severity: Notice
Message: Undefined property: Public_homepage::$sites
Filename: controllers/public_homepage.php
Line Number: 15
Fatal error: Call to a member function get() on a non-object in /var/www/businessbuilderapp.com/public_html/application/controllers/public_homepage.php on line 15
or this (error2):
A PHP Error was encountered
Severity: Notice
Message: Undefined property: Businessbuilder::$db
Filename: core/Model.php
Line Number: 50
Fatal error: Call to a member function query() on a non-object in /var/www/businessbuilderapp.com/public_html/application/models/bba_model.php on line 25
My theory as to why I am getting these errors is because the instance of the object is different than the one that loaded the model and libraries. What's odd about that though is that arrays are carried over, but not objects. So in the core Loader.php of codeigniter array $_ci_models is populated with models that are not loaded in the Public_homepage class
Also what might help you is that from the first pass through the businessbuilder class, I am able to load and use the modules successfully, but when Public_homepage is called, that's when things start to fail.
What makes this confusing is that I'm trying to figure out 2 errors with one question which is probably my mistake. Here is a description of when I get the errors:
Error1:
When I run the code as is, I cannot call the sites property.
Error2:
When I change the
call_user_func_array(array(&$CI, $function), array_slice($RTR->uri->rsegments, 2));
to
eval($class . "->" . $function);
I understand that this is really confusing, especially when I explain it, but if you need more info, please let me know. Also note that the Public_homepage looks like that because I am testing. There's no need to dump more useless lines if the error can be produced with minimal code.
Update
After reading some of the answers, I realized that I didn't explain the code. What this code does is that it allows me to store different urls inside a database, but all the urls stored there can call the same page even though they are different. I guess an exact example would be changing the slug on wordpress.
What happens is that the businessbuilder class is set to accept ALL requests to the server. When it hits the businessbuilder class, it will access the database, find out what sub url you are using, find the real controller that the user is looking for, and access that controller.
So after lots of searching, I think I have a workaround. The issue what what I thought with the instance. After diving into the framework I realized that it is storing the instance into as static var, private static $instance. I modified the constructor to not overwrite if that var has been populated. On top of that, since there were some oddities still with the loading, for some reason objects would be marked as loaded but in reality were not, I had to add a new var to the controller, protected $ci_instance. In the end, I modified the CI_Controller to look like the following:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* CodeIgniter
*
* An open source application development framework for PHP 5.1.6 or newer
*
* #package CodeIgniter
* #author ExpressionEngine Dev Team
* #copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
* #license http://codeigniter.com/user_guide/license.html
* #link http://codeigniter.com
* #since Version 1.0
* #filesource
*/
// ------------------------------------------------------------------------
/**
* CodeIgniter Application Controller Class
*
* This class object is the super class that every library in
* CodeIgniter will be assigned to.
*
* #package CodeIgniter
* #subpackage Libraries
* #category Libraries
* #author ExpressionEngine Dev Team
* #link http://codeigniter.com/user_guide/general/controllers.html
*/
class CI_Controller {
private static $instance;
protected $ci_instance; // line added
/**
* Constructor
*/
public function __construct()
{
if(self::$instance == null) // line added
self::$instance =& $this;
$this->ci_instance =& get_instance(); // line added
// Assign all the class objects that were instantiated by the
// bootstrap file (CodeIgniter.php) to local class variables
// so that CI can run as one big super object.
foreach (is_loaded() as $var => $class)
{
$this->$var =& load_class($class);
}
$this->load =& load_class('Loader', 'core');
$this->load->_base_classes =& is_loaded();
$this->load->_ci_autoloader();
log_message('debug', "Controller Class Initialized");
}
public static function &get_instance()
{
return self::$instance;
}
}
// END Controller class
/* End of file Controller.php */
/* Location: ./system/core/Controller.php */
The only issue so far is that I cannot do $this->load->model("some_model");. Instead I have to use $this->ci_instance->load->model("some_model"); and everything will stem from there. I don't really care about the extra var, but what I don't like is modifying out of box solutions because it increases the complexity to do an upgrade.
for now I've marked this as an answer because it is what "I" have chosen to use as my solution, but I am still opened to a better solution than the one I am using. An exact description of what needs to be solved is as follows:
Copy all loaded properties from one instance to another. Basically do a merger of two instances if possible.
If someone can answer that with a better solution than mine, preferably without modifying the codeigniter core, I'd gladly change my answer because I am not happy with my solution because I don't know what effects I might encounter later on during development.
In your application/autoload.php set codeigniter to load database class.
$autoload['libraries'] = array('database', 'otherlibrary', 'otherlibrary2');
It must be all you need to solve your problem.
if u using HMVC just using
Class Models extends MX_Loader{
function getUser($username){
$sql="SELECT
*
FROM
user
WHERE username = ? "
return $this->db->query($sql,array($username))->row();
}
}