What is the proper syntax to get a db instance? - php

I use eloquent outside laravel.
Here is the file responsible of it:
class DbSql
{
public function db()
{
$capsule = new Capsule;
$capsule->addConnection( [
'driver' => Settings::DATABASE_DRIVER,
'host' => Settings::DATABASE_HOST,
'database' => Settings::DATABASE_NAME,
'username' => Settings::DATABASE_USERNAME,
'password' => Settings::DATABASE_PASSWORD,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
] );
# Make this Capsule instance available globally
$capsule->setAsGlobal();
return $capsule;
}
}
In controllers, I instantiate the instance once, and may use it several time in a controller:
$dbInstance = ( new DbSql() )->db(); // Create the instance
// Instance usages:
$images = $dbInstance->table( 'images_attachments' )->where...
$files = $dbInstance->table( 'files' )->where...
.....
Should I instantiate with:
$dbInstance = ( new DbSql() )->db(); as in the code above
or with:
$dbInstance = ( new DbSql() )->db()->getConnection();
what's the proper way ?

First of all, there is no need to wrap the Capsule (which is a Illuminate\Database\Capsule\Manager instance) in another class to make easier to retrieve it. In fact, the Illuminate\Database\Capsule\Manager provides a singleton instance and simple accessors.
Once you call
$capsule->setAsGlobal();
the Capsule instance will be accessible statically from everywhere via Capsule::.
This because the setAsGlobal() method, under the hood, does simply this:
static::$instance = $this;
Saving the instance in a static class variable to retrieve it on subsequent calls to Capsule::.
So, once you call setAsGlobal(), it is possible to do, for example:
Capsule::select('select * from user');
and you'll get the results of the query on the previously created connection.
However, you can see that the select method doesn't exists on the Manager (alias Capsule) class.
Why does it works?
Because the Manager class overrides the __callStatic() php magic method
class Manager
{
use CapsuleManagerTrait;
// ...
public static function __callStatic($method, $parameters)
{
return static::connection()->$method(...$parameters);
}
}
So the call to select() method is redirected to the underlying \Illuminate\Database\Connection instance automagically.
This means that:
there is no need to wrap the Manager/Capsule in an external class, since it is already a wrapper over the Connection class. No need to add another wrapper.
If you prefer to wrap it, however, I suggest you to returning the Manager/Capsule instance and not the underlying connection, since the suggested approach in the docs is to work directly with the Manager

Related

Accessing Eloquent ORM inside Ratchet's MessageComponentInterface

The title is confusing. Basically, I'm unable to access Laravel's utilities in classes that don't belong to the framework.
Error is: Call to a member function connection() on null in C:\xampp\htdocs\blazocket\vendor\laravel\framework\src\Illuminate\Databas e\Eloquent\Model.php:1571
I have a Websocket Server based on Ratchet library, it has the following strucutre:
namespace App\Http\Websockets;
$server = new \Ratchet\App('localhost', 8080);
$server->route('/api/socket', new WebSocketHandler, array('*'));
Snippet from WebSocketHandler:
namespace App\Http\Websockets;
use App\Models\Website;
class WebSocketHandler implements MessageComponentInterface {
protected $apis;
protected $clients;
public function __construct() {
$this->refreshDatabase();
$this->clients = new \SplObjectStorage;
}
private function refreshDatabase(){
$this->apis = Website::all();
}
}
I've tried doing this:
$app = require dirname( __FILE__ ) . '/../../../bootstrap/app.php';
$app->boot();
But It's not the solution (I tried it from a different SO issue). Also, I've read this, but I'm not sure about the way I could implement the "bootstraping" thing.
I'm aware that the error is happening because there's no connection between the class (WebSocketHandler) and the Laravel framework. I've tried every answer, and checking the docs, I couldn't find a way on my own to make this class connected.
Your help is greatly appreciated.
You have missed declaring the namespace. As you see here
there's a namespace declaration.
The obvious answer that you need to boot (Idk the correct term) Laravel and make it available for custom classes that have nothing to do with Laravel.
I do have this way for the database connection only. First, add these on the top of your class:
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Events\Dispatcher;
use Illuminate\Container\Container;
Then, create a new method in your class and call it once you initialize it.
private function setupLaravel(){
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'mydatabase',
'username' => 'root',
'password' => "",
]);
$capsule->setEventDispatcher(new Dispatcher(new Container));
$capsule->setAsGlobal();
$capsule->bootEloquent();
}
If you're running Ratchet and want to access the DB via Eloquent ORM, this is the way.

Is it possible to use PHP-DI definitions instead of Eloquents Capsule Managers Built in IoC within Slim 3?

I am currently working on a slim 3 application that is using a php-di bridge to serve dependencies. Everything is working flawlessly within controllers, however, I came across an issue when trying to add dependencies to a model observer. Eloquents capsule manager has its own container so I am unable to pass through dependencies (not injected) without it throwing an error.
Current setup:
$app = new \App\App; // extends \DI\Bridge\Slim\App
$container = $app->getContainer();
$capsule = new Capsule;
$capsule->addConnection($settings['settings']['db']);
$capsule->setEventDispatcher(new Dispatcher());
$capsule->setAsGlobal();
$capsule->bootEloquent();
// Sale model extends eloquent model
Sale::observe(new SaleObserver($container));
SaleObserver Class:
class SaleObserver {
protected $container;
public function __construct($container){
$this->container = $container;
}
public function saving(Sale $sale){
// Mail logic using container
}
}
This gives me the error triggered from my SaleObserver:
Unresolvable dependency resolving [Parameter #0 [ <required> $container ]]
I think the problem is that the SaleObserver is being resolved my eloquent's own container which is not allowing me to pass my PHP-DI "$container" through. Is something like this possible within slim 3 without a hacky approach?
I am testing with container just to see if I can pass something through, however, my main objective is to simply pass through my mail definition since it has all the configs already setup.
Update:
I have previously tried to type hint within the SaleObserver class like below in hopes that php-di would catch it with no avail.
use App\Mail\Contracts\MailerInterface;
class SaleObserver {
protected $mail;
public function __construct(MailerInterface $mail){
$this->mail = $mail;
}
public function updating(Sale $sale){
// Send Mail logic
}
}
I end up getting a different error that suggests that php-di auto wiring is not working with classes that are not not connected to a route even though auto wiring is set to true because the injected parameter is not automatically instantiating as it should.
This shows the error:
Uncaught ArgumentCountError: Too few arguments to function
The bridge I am using is PHP-DI/Slim-Bridge
Unfortunately, seems like Laravel's database event handling mechanism heavily relies on Laravel's container implementation. After reading this great post, I managed to make events work as expected.
I set up an instance of \Illuminate\Container\Container and added it to and used it to my application container ($container). This instance of Laravel container is going to be used only for handling events. Now I can set up an instance of \Illuminate\Events\Dispatcher and register it with setting up the Manager instance:
$container['laravel-container'] = function ($c){
return new \Illuminate\Container\Container;
};
$container['database-event-dispatcher'] = function ($c) {
return new \Illuminate\Events\Dispatcher($c['laravel-container']);
};
$capsule = new \Illuminate\Database\Capsule\Manager();
$capsule->addConnection($container['settings']['db']);
// Pass event dispatcher instance from application container
$capsule->setEventDispatcher($container['database-event-dispatcher']);
$capsule->setAsGlobal();
// Now that we're using events it is important to call $capsule->bootEloquent();
$capsule->bootEloquent();
A when creating a new instance of the observer, we can pass our applications $container, or only pass the objects we need in the observer.
So, assuming this is our SaleObserver class:
class SaleObserver {
protected $mail;
public function __construct(MailerInterface $mail){
$this->mail = $mail;
}
public function updating(Sale $sale){
// Send Mail logic
}
}
we should give it the mailer instance:
$container ['mailer'] = function ($c) {
// set up and return a MailerInterface
};
Sale::observe(new SaleObserver($container['mailer']));
It's possible to use just the Connection object with an empty Laravel container like this:
use Illuminate\Database\Connection;
use Illuminate\Database\Connectors\ConnectionFactory;
use Psr\Container\ContainerInterface as Container;
$container[Connection::class] = function (Container $container) {
$settings = $container->get('settings');
$config = [
'driver' => 'mysql',
'host' => $settings['db']['host'],
'database' => $settings['db']['database'],
'username' => $settings['db']['username'],
'password' => $settings['db']['password'],
'charset' => $settings['db']['charset'],
'collation' => $settings['db']['collation'],
'prefix' => '',
];
$factory = new ConnectionFactory(new \Illuminate\Container\Container());
$connection = $factory->make($config);
// Disable the query log to prevent memory issues
$connection->disableQueryLog();
return $connection;
};

Can we Integrate Laravel project as a library in CodeIgniter?

I want to increase the functionality of my CodeIgniter project by integrating some code that is written in laravel? how do I approach,
Can I include the code via library in CodeIgniter ? If yes How?
I only want to include controllers and ORM into the CI.
Laravel code is a kind of api fetcher with function talks with other
3rd party services.
Yes you can use composer to install Laravel specific modules/projects, third-party projects in your CodeIginter. Just include autoload in your `index.php' file at top
// Composer autoload
require_once __DIR__.'/vendor/autoload.php';
I am using Eloquent as ORM in my CodeIgniter codebase.
Create a classmap to your app directory in composer.json
"autoload": {
"psr-4": { "YourApp\\": ["application/"] },
Use Eloquent
To use Eloquent, you will require to create a library to setup Eloquent for use.
/**
* Capsule setting manager for Illuminate/database
*/
use Illuminate\Database\Capsule\Manager as CapsuleManager;
use Illuminate\Events\Dispatcher;
use Illuminate\Container\Container;
class Capsule extends CapsuleManager {
public function __construct()
{
parent::__construct();
//Loaded by CI
if(function_exists('get_instance')) {
$ci = &get_instance();
$db = new stdClass;
$db = $ci->db;
} else {
require_once __DIR__.'/../config/database.php';
$db = (object) $db['default'];
}
$this->addConnection(array(
'driver' => $db->dbdriver,
'host' => $db->hostname,
'database' => $db->database,
'username' => $db->username,
'password' => $db->password,
'charset' => $db->char_set,
'collation' => $db->dbcollat,
'prefix' => $db->dbprefix,
));
$this->setEventDispatcher(new Dispatcher(new Container));
// Make this Capsule instance available globally via static methods... (optional)
$this->setAsGlobal();
// Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher())
$this->bootEloquent();
}
}
// END Capsule Class
Now load the auto load the library, and you have the eloquent beauty.
Similarly, you can use MonoLog for logging, Whoops for error display, Formers\Former for form building etc.
Use Whoops
You can place this code somewhere after autload and defining CI Environment in your index.php to use beautiful https://github.com/filp/whoops library
if (ENVIRONMENT == 'development') {
$whoops = new \Whoops\Run;
$whoops->pushHandler(new Whoops\Handler\PrettyPageHandler());
$whoops->register();
}
You can also extend CI_Router to use Laravel style routing in your Code Igniter app.
Blade Templating
You can extend the CI_Loader to use Blade templating in Code Igniter. Create a new file MY_Loader in your application/core directory with this code.
use Illuminate\Blade\Environment;
use Illuminate\Blade\Loader;
use Illuminate\Blade\View;
class MY_Loader extends CI_Loader {
public function __construct()
{
parent::__construct();
}
public function blade($view, array $parameters = array())
{
$CI =& get_instance();
$CI->config->load('blade', true);
return new View(
new Environment(Loader::make(
$CI->config->item('views_path', 'blade'),
$CI->config->item('cache_path', 'blade')
)),
$view, $parameters
);
}
}
You may have to create a config file blade.php in your application/config directory to store blade specific configurations.
//config/blade.php
$config['views_path'] = APPPATH . 'views/blade/';
$config['cache_path'] = APPPATH . 'cache/blade/';
Now you can do something like this in your controller
class Home extends CI_Controller {
public function index()
{
// Prepare some test data for our views
$array = explode('-', date('d-m-Y'));
list($d, $m, $y) = $array;
// Basic view with no data
echo $this->load->blade('home.index');
// Passing a single value
echo $this->load->blade('home.index')->with('day', $d);
// Multiple values with method chaining
echo $this->load->blade('home.index')
->with('day', $d)
->with('month', $m)
->with('year', $y);
// Passing an array
echo $this->load->blade('home.index', array(
'day' => $d,
'month' => $m,
'year' => $y
));
}
}

Zend Framework 2 Access DB from Custom Library

I am migrating a project from Zend framework 1.4 to 2.4, I have a class in "vendor/custom/classes/User.php"
<?php
namespace Classes;
use Zend\Db\TableGateway\TableGateway;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\Adapter\Adapter;
class User
{
public function getItemById($id)
{
//$config = $this->getServiceLocator()->get('config');
//This only work in controller
$configs = array();
$adapter = new Adapter($configs);
$projectTable = new TableGateway('project', $adapter);
$rowset = $projectTable->select(array('type' => 'PHP'));
echo 'Projects of type PHP: ';
foreach ($rowset as $projectRow) {
echo $projectRow['name'] . PHP_EOL;
}
}
}
?>
I need to load merged configurations in my files in "config/autoload" , global.php and local.php. $config = $this->getServiceLocator()->get('config'); Can someone guide me how can I get these configurations from a custom class. Basically I am trying to do is writing set of classes like User, Project, Customer outside of Models and use them commonly in all modules like CMS, Admin Panel, Web site. Appreciate your guidance.
An approach could be using a factory.
You create a class UserFactory implementing Zend\ServiceManager\FactoryInterface. This class will have a method createService with a $serviceLocator parameter. You use this service locator to retrieve your dependencies and pass them to your User class.
In your User class you need to use a controller that accepts as parameters the dependencies that you need to pass to it
Since there is no direct way to access those configurations. I have wrote constants with DB access information in the local and global php files in config/autoload and used it in my class.
class DBManager
{
protected $adapter ;
protected $connection ;
/**
* Constructor
*/
public function __construct()
{
$configs = array(
'hostname' => DB_SERVER_NAME,
'driver' => 'Pdo_Mysql',
'database' => DB_DATABASE_NAME,
'username' => DB_USER_NAME,
'password' => DB_PASSWORD ,
);
$this->adapter = new Adapter($configs);
}
}

Is possible to use PHQL in a non PhalconPHP project?

So this is my question. I have a legacy project with more than 700K PHP lines. Homemade framework.
Our ORM works, but I'd like to try the PhalconPHP in the project.
We are using Pimple as DI.
Is it possible to run just the Model/PHQL in a non Phalcon project?
If so, how?
Thanks
Yes, it is possible. Thanks to #calin in the PhalconPHP forum I found the answer. Basically the steps are the following ones:
Create an instance of the Phalcon DI (or any that extends the DI interface)
Set the modelsManager
Set the modelsMetadata
Set the db
Set the default DI
In the model, define the getSource if necessary.
Example:
<?php
$di = new \Phalcon\DI();
$di->set('modelsManager', function () {
return new \Phalcon\Mvc\Model\Manager();
});
$di->set('modelsMetadata', function () {
return new \Phalcon\Mvc\Model\MetaData\Memory();
});
$di->set('db', function () {
return new \Phalcon\Db\Adapter\Pdo\Mysql(array(
'adapter' => 'Mysql',
'host' => 'xxx.dev',
'username' => 'xxxx',
'password' => 'yyy',
'dbname' => 'zzz',
));
});
\Phalcon\DI::setDefault($di);
That has to be called in some part before you will need the call to the Models/PHQL.
Define a normal Model, and call the models. It's pretty epic.
Model example:
<?php
namespace Vendor\App\Mvc\Model;
use \Phalcon\Mvc\Model;
class UserModel extends Model
{
public function getSource()
{
return "users";
}
}
And in some part of the code, after the calls to the Phalcon DI:
<?php
echo \Vendor\App\Mvc\Model\UserModel::find()->count();

Categories