I have designed following class
<?php
class DB {
private static $objInstance;
/*
* Class Constructor - Create a new database connection if one doesn't exist
* Set to private so no-one can create a new instance via ' = new DB();'
*/
private function __construct() {}
/*
* Like the constructor, we make __clone private so nobody can clone the instance
*/
private function __clone() {}
/*
* Returns DB instance or create initial connection
* #param
* #return $objInstance;
*/
public static function getInstance( ) {
if(!self::$objInstance){
$ini_array = parse_ini_file("db.ini");
$dbInfo['server'] = $ini_array['server'];
$dbInfo['database'] = $ini_array['database'];
$dbInfo['username'] = $ini_array['username'];
$dbInfo['password'] = $ini_array['password'];
$dsn = 'mysql:host='.$dbInfo['server'].';dbname='.$dbInfo['database'].'';
self::$objInstance = new PDO(DB_DSN, $dbInfo['username'], $dbInfo['password']);
self::$objInstance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$objInstance;
} # end method
/*
* Passes on any static calls to this class onto the singleton PDO instance
* #param $chrMethod, $arrArguments
* #return $mix
*/
final public static function __callStatic( $chrMethod, $arrArguments ) {
$objInstance = self::getInstance();
return call_user_func_array(array($objInstance, $chrMethod), $arrArguments);
} # end method
}
?>
My problem is when I want to perform a query I get following error:
Fatal error: Call to undefined method DB::query()
foreach(DB::query("SELECT * FROM content_type_video") as $row){
print_r($row);
}
Any ideas why and how to fix this?
$db = DB::getInstance();
foreach($db->query("SELECT * FROM content_type_video") as $row){
print_r($row);
}
The functionality you want to make use of is not available in your PHP version - you need to upgrade to at least PHP 5.3 for __callStatic.
However I suggest you to stop using singletons anyway and stop using static function calls everywhere in your code.
Related
wel i have simple problem
i'm trying to load this class in to my own class: https://github.com/picqer/moneybird-php-client/blob/master/src/Picqer/Financials/Moneybird/Moneybird.php
logicaly is to call it in __construct:
\Picqer\Financials\Moneybird\Moneybird $moneybird
but the problem is that it requires \Picqer\Financials\Moneybird\Connection to be established beforehand
so how can i use it in my code:
class OnepageControllerSuccessAction implements \Magento\Framework\Event\ObserverInterface
{
protected $_order;
protected $_connection;
protected $_invoiceFactory;
protected $_moneybird;
public function __construct(
\Magento\Sales\Api\Data\OrderInterface $order,
\Picqer\Financials\Moneybird\Connection $connection,
\Picqer\Financials\Moneybird\Moneybird $moneybird,
\Wemessage\Moneybird\Model\InvoiceFactory $invoiceFactory
){
$this->_order = $order;
$this->_connection = $connection;
$this->_invoiceFactory = $invoiceFactory;
$this->_moneybird = $moneybird;
}
...
public function execute(
\Magento\Framework\Event\Observer $observer
) {
$orderids = $observer->getEvent()->getOrderIds();
foreach($orderids as $orderid){
$order = $this->_order->load($orderid);
$this->_connection->setRedirectUrl('');
...
// connection has been established and now we need to pass it to the moneybird
$moneybird = $this->_moneybird($this->_connection);
which results in fault: PHP Fatal error: Uncaught Error: Function name must be a string...
wel one way to fix it, is to add method setConnection in that class and call it, but the problem that it's being downloaded from repository by composer so if i want to use my module in another installation i will have to do same steps.
any other workaround ?
added construct of moneybird file:
/**
* Moneybird constructor.
* #param \Picqer\Financials\Moneybird\Connection $connection
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
so it will not lead to confusions and unnecessary questions.
How do I load a database container using PHP DI?
This is one of the variations I have tried up until now.
Settings.php
<?php
use MyApp\Core\Database;
use MyApp\Models\SystemUser;
return [
'Database' => new Database(),
'SystemUser' => new SystemUser()
];
init.php
$containerBuilder = new \DI\ContainerBuilder();
$containerBuilder->addDefinitions('Settings.php');
$container = $containerBuilder->build();
SystemUserDetails.php
<?php
namespace MyApp\Models\SystemUser;
use MyApp\Core\Database;
use MyApp\Core\Config;
use MyApp\Helpers\Session;
/**
*
* System User Details Class
*
*/
class SystemUserDetails
{
/*=================================
= Variables =
=================================*/
private $db;
/*===============================
= Methods =
================================*/
/**
*
* Construct
*
*/
public function __construct(Database $db)
{
# Get database instance
// $this->db = Database::getInstance();
$this->db = $db;
}
/**
Too few arguments to function MyApp\Models\SystemUser\SystemUserDetails::__construct(), 0 passed in /www/myapp/models/SystemUser.php on line 54 and exactly 1 expected
File: /www/myapp/models/SystemUser/SystemUserDetails.php
Shouldn't the database get loaded automatically?
Trace:
Currrently, My main index.php file extends init.php which is the file where it create the container (pasted code part in the post).
Then I call the App class, which fetches the URL(mysite.com/login/user_login) and instantiate a new controller class and run the mentioned method, in this case, it's the first page - MyApp/Contollers/Login.php.
The user_login method fetches the credentials, validate them, and if they are valid, uses the SystemUser object to login.
SystemUser class:
namespace MyApp\Models;
class SystemUser
{
public $id;
# #obj SystemUser profile information (fullname, email, last_login, profile picture, etc')
protected $systemUserDetatils;
public function __construct($systemUserId = NULL)
{
# Create systemUserDedatils obj
$this->systemUserDetatils = new \MyApp\Models\SystemUser\SystemUserDetails();
# If system_user passed
if ( $systemUserId ) {
# Set system user ID
$this->id = $systemUserId;
# Get SysUser data
$this->systemUserDetatils->get($this);
} else {
# Check for sysUser id in the session:
$systemUserId = $this->systemUserDetatils->getUserFromSession();
# Get user data from session
if ( $systemUserId ) {
# Set system user ID
$this->id = $systemUserId;
# Get SysUser data
$this->systemUserDetatils->get($this);
}
}
}
}
PHP-DI is working correctly.
In your SystemUser class you are doing:
$this->systemUserDetatils = new \MyApp\Models\SystemUser\SystemUserDetails();
The constructor for SystemUserDetails requires a Database object, which you are not passing.
By calling new directly, you are not using PHP-DI. By doing this you are hiding the dependency, which is exactly what you are supposedly trying to avoid if you want to use a dependency injection system.
If SystemUser depends ("needs") SystemUserDetails, the dependency should be explicit (e.g. declared in its constructor).
Furthermore: You do not need a definitions file for a system like this. And the definitions file you show in your question doesn't follow the best practices recommended by PHP-DI.
Your design is far from perfect, and I'm not sure of your end-goals, but if you did something like this, it could work:
<?php
// src/Database.php
class Database {
public function getDb() : string {
return 'veryDb';
}
}
<?php
// src/SystemUserDetails.php
class SystemUserDetails {
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function getDetails() {
return "Got details using " . $this->db->getDb() . '.';
}
}
<?php
// src/SystemUser.php
class SystemUser {
protected $details;
public function __construct(SystemUserDetails $details, $userId=null) {
$this->details = $details;
}
public function getUser() {
return "Found User. " .$this->details->getDetails();
}
}
<?php
//init.php
require_once('vendor/autoload.php');
// build the container. notice I do not use a definition file.
$containerBuilder = new \DI\ContainerBuilder();
$container = $containerBuilder->build();
// get SystemUser instance from the container.
$userInstance = $container->get('SystemUser');
echo $userInstance->getUser(), "\n";
Which results in:
Found User. Got details using veryDb.
I want to connect to different database according to URL. I try to set request attribute and get that attribute in *Factory.php.
I edit autoload/pipeline.php:
<?php
$app->pipe(UrlHelperMiddleware::class);
$app->pipe(\App\Action\Choose::class);
$app->pipeDispatchMiddleware();
in Choose.php I implement process() like this:
<?php
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
/** #var RouteResult $route */
$route = $request->getAttribute(RouteResult::class);
if ($route->getMatchedRouteName() == 'FirstRoute' or $route->getMatchedRouteName() == 'SecondRoute') {
$request = $request->withAttribute('DB_NAME', $route->getMatchedParams()['param']);
}
return $delegate->process($request);
}
The main problem is in *Factory.php I don't access to request.
Any try to access to Interop\Http\ServerMiddleware\MiddlewareInterface or Psr\Http\Message\ServerRequestInterface in *Factory.php raises same error.
Is there any way pass parameter from pipeline middleware to factory class?
If you use zend-servicemanager you can try this (just a theory, not tested):
Create 2 database factories in your config:
'db.connection.a' => DbFactoryA::class,
'db.connection.b' => DbFactoryB::class,
Then depending on the route, in Choose you load the connection you need and pass it to the container as the default connection.
$db = $container->get('db.connection.a');
$container->setService('db.connection.default', $db);
And now in all following middleware you can grab the default connection.
UPDATE:
As mentioned in the comments, this requires the container to be injected which is considered bad practice. How about you wrap the two in a common connection class and set the required from Choose:
class Connection
{
private $connection;
/**
* #var ConnectionInterface
*/
private $db_a;
/**
* #var ConnectionInterface
*/
private $db_b;
public function __construct(ConnectionInterface $a, ConnectionInterface $b)
{
$this->db_a = $a;
$this->db_b = $b;
}
public function setConnection($connection)
{
if ($connection === 'a') {
$this->connection = $this->db_a;
return;
}
$this->connection = $this->db_b;
}
public function getConnection()
{
return $this->connection;
}
}
Or you can inject only the config and create the database connection that's really needed. Store it in a property for caching (like the container does).
I am currently working in laravel 5.3. we are following this approach : controller ->service ->repository -> modal . but i do not what are we passing in the construct methods in each class.
in the below codes the flow goes like this: paycontroller -> Merchant service ->MerchantRepository->modal
the first one is paycontroller:
class PayController extends Controller
{
private $merchantService;
private $paymentService;
private $pay_request_field = array(
'orderID', 'hashKey','currencyCode','amount'
);
/**
* Create a new controller instance.
*
* #return void
*/
// public function __construct()
// {
// $this->middleware('auth');
// }
public function __construct(MerchantService $merchantService, PaymentService $paymentService){
$this->merchantService = $merchantService;
$this->paymentService = $paymentService;
}
is the constructor receiving a variable of the MerchantService and Payment service? if so where is the value coming from? im confused here
next is the MerchantService:
class MerchantService
{
private $merchantRepository;
private $merchantConfigRepository;
private $merchantPaymentRepository;
private $merchant;
private $merchantConfig;
private $merchantPayment;
public function __construct(MerchantRepository $merchantRepository, MerchantConfigRepository $merchantConfigRepository, MerchantPaymentRepository $merchantPaymentRepository){
$this->merchantRepository = $merchantRepository;
$this->merchantConfigRepository = $merchantConfigRepository;
$this->merchantPaymentRepository = $merchantPaymentRepository;
}
public function getMerchantById($id){
$this->merchant = $this->merchantRepository->getMerchantById($id);
$this->merchantConfig = $this->merchantConfigRepository->getMerchantConfig($this->merchant->mid);
return $this->merchant->toArray();
then the MerchantRepository:
class MerchantRepository
{
private $merchant;
public function __construct(Merchant $merchant){
$this->merchant = $merchant;
}
public function getMerchantByHash($hashKey="",$status='action'){
return $this->merchant->where([["hashKey","=",trim($hashKey)],["status","=",$status]])->firstOrFail();
}
public function getMerchantById($mid="",$status='action'){
return $this->merchant->where([["mid","=",trim($mid)],["status","=",$status]])->firstOrFail();
}
}
Then finally the Modal:
class Merchant extends Model
{
protected $connection = 'mysql1';
//Table Name
protected $table = 'merchants';
//Primary Key
protected $primaryKey = 'mid';
}
so whats my overall question is, what is going on in this whole process, and the constructors( parameters) where are they coming from.
Thanks in advance
In this example you are initializing the User and Database using the new keyword
Like:
$database = new Database('host', 'user', 'pass', 'dbname');
$user = new User($database);
And using the new keyword you need to manually resolve the dependency of the class you are initializing.
I have just pasted a line from here:
http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.new
An object will always be created unless the object has a constructor
defined that throws an exception on error. Classes should be defined
before instantiation (and in some cases this is a requirement)
Here for this block of code:
public function __construct(MerchantService $merchantService, PaymentService $paymentService){
$this->merchantService = $merchantService;
$this->paymentService = $paymentService;
}
You are adding it to the constructor of your Controller and you are lucky that all the dirty works are done for you by Laravel. i.e. dependency of the Controller will be automatically resolved for you by Laravel.
Laravel 5 has great dependency injection and you don't need to worry about from where the dependency is resolved.
Just have a look at this dependency Injection
http://slashnode.com/dependency-injection/
I hope you understood the differences.
class User
{
private $database = null;
public function __construct(Database $database) {
$this->database = $database;
}
public function getUsers() {
return $this->database->getAll('users');
}
}
$database = new Database('host', 'user', 'pass', 'dbname');
$user = new User($database);
$user->getUsers();
so in the above example, we are actually initiating a database object and passing it to the class's constructor. so my question is wwhere is this value of $merchant service being initiated and coming from ?
public function __construct(MerchantService $merchantService, PaymentService $paymentService){
$this->merchantService = $merchantService;
$this->paymentService = $paymentService;
}
in the above example of class, we can actually see that the $database is actually initiated outside the class...so i just dont understand where is the $merchantservice being initiated...
There is a shortcut method to create an object from a method that return a string?
For the moment, I used that :
class MyClass {
/**
* #return string
*/
public function getEntityName() {
return 'myEntityName';
}
}
$myClassInstance = new MyClass();
// Need to get string
$entityName = $myclassInstance->getEntityName();
// And after I can instantiate it
$entity = new $entityName();
There is short-cut syntax for getting the string but not for creating the object from the string to date in PHP. See the following code in which I also include a 'myEntityName' class:
<?php
class myEntityName {
public function __construct(){
echo "Greetings from " . __CLASS__,"\n";
}
}
class MyClass {
/**
* #return string
*/
public function getEntityName() {
return 'myEntityName';
}
}
$entityName = ( new MyClass() )->getEntityName();
$entity = new $entityName();
With one line the code instantiates a MyClass object and executes its getEntityName method which returns the string $entityName. Interestingly, if I replace my one-liner with the following it fails in all versions of PHP except for the HipHop Virtual Machine (hhvm-3.0.1 - 3.4.0):
$entityName = new ( ( new MyClass() )->getEntityName() );