Trying to use doctrine with slim 4 and php-di I don't get it running with autowire.
Following my setup:
index.php
$definitions = [
'settings' => [
'doctrine' => [
'dev_mode' => true,
'cache_dir' => __DIR__.'/../var/cache/doctrine',
'metadata_dirs' => [__DIR__.'/../src/Domain/'],
'connection' => [
'driver' => 'pdo_mysql',
'host' => 'webdb',
'port' => 3306,
'dbname' => 'db',
'user' => 'user',
'password' => 'pass',
]
]
],
EntityManagerInterface::class => function (ContainerInterface $c): EntityManager {
$doctrineSettings = $c->get('settings')['doctrine'];
$config = Setup::createAnnotationMetadataConfiguration(
$doctrineSettings['metadata_dirs'],
$doctrineSettings['dev_mode']
);
$config->setMetadataDriverImpl(
new AnnotationDriver(
new AnnotationReader,
$doctrineSettings['metadata_dirs']
)
);
$config->setMetadataCacheImpl(
new FilesystemCache($doctrineSettings['cache_dir'])
);
return EntityManager::create($doctrineSettings['connection'], $config);
},
UserRepositoryInterface::class => get(UserRepository::class)
then my repository:
class UserRepository extends \Doctrine\ORM\EntityRepository implements UserRepositoryInterface {
public function get($id){
$user = $this->_em->find($id);
...
}
}
Currently I get the follwoing error message:
"Doctrine\ORM\Mapping\ClassMetadata" cannot be resolved: Parameter $entityName of __construct() has no
value defined or guessable
Full definition:
Object (
class = Doctrine\ORM\Mapping\ClassMetadata
lazy = false
...
can somebody tell me how to solve that issue respectively is there any other maybe cleaner/easier way to integrate doctrine using php-di?
Update
Referring to the hint that ClassMetadata can't be autowired I changed the structure as follows:
index.php
$definitions = [
EntityManager::class => DI\factory([EntityManager::class, 'create'])
->parameter('connection', DI\get('db.params'))
->parameter('config', DI\get('doctrine.config')),
'db.params' => [
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => '',
'dbname' => 'foo',
],
'doctrine.config' => Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src/core/models/User"), true),
...
userservice/core/models/User.php:
namespace userservice\core\models;
use userservice\core\exceptions\ValidationException;
use \DateTime;
use ORM\Entity;
/**
* #Entity(repositoryClass="userservice\infrastructure\repositories\UserRepository")
*/
class User extends Model{
/**
* #Column(type="string", length="50")
* #var string
*/
private $name;
...
And the userservice/infrastructure/UserRepository.php:
namespace userservice\infrastructure\repositories;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\ORMInvalidArgumentException;
use userservice\core\models\User;
use userservice\core\repositories\UserRepositoryInterface;
use userservice\infrastructure\repositories\Repository;
class UserRepository extends Repository implements UserRepositoryInterface {
private $_repository;
/**
*
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager) {
parent::__construct($entityManager);
$this->_repository = $entityManager->getRepository('User'); // OR $this->entityManager->find('User', 1);
}
Now I'm getting the following error in UserRepository construct (getRepository):
Uncaught Doctrine\Persistence\Mapping\MappingException: Class 'User' does not exist in C:\projects\microservices\user-service\vendor\doctrine\persistence\lib\Doctrine\Persistence\Mapping\MappingException.php
How can I get doctrine find the entities?
Related
I have succeed to connect mongodb database with zend framework 3 directly without using any module. I have accessed that connection in controller by using factory. But I want to access this in every modules controllers and models i.e I want to access it globally. How can I access "$this->db" globally. Where to write the "connect" method to access that variable globally. I am new to zend framework 3 and if any one have any solution that will be very helpful to proceed with my project. I am sharing the files I have used.
global.php
use Zend\Session\Storage\SessionArrayStorage;
use Zend\Session\Validator\RemoteAddr;
use Zend\Session\Validator\HttpUserAgent;
return [
'config' => [
'driver' => 'mongodb',
'host' => '127.0.0.1',
'port' => 27017,
'username' => 'test',
'password' => 'test',
'dbname' => 'test',
'connection_string'=> sprintf('mongodb://%s:%d/%s','127.0.0.1','27017','test')
],
// Session configuration.
'session_config' => [
// Session cookie will expire in 1 hour.
'cookie_lifetime' => 60*60*1,
// Session data will be stored on server maximum for 30 days.
'gc_maxlifetime' => 60*60*24*30,
],
// Session manager configuration.
'session_manager' => [
// Session validators (used for security).
'validators' => [
RemoteAddr::class,
HttpUserAgent::class,
]
],
// Session storage configuration.
'session_storage' => [
'type' => SessionArrayStorage::class
],
];
UserControllerFactory.php
<?php
namespace Application\Controller;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\AuthManager;
/**
* This is the factory for UserController. Its purpose is to instantiate the
* controller.
*/
class UserControllerFactory implements FactoryInterface{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null){
$config = $container->get('config');
$db_config = $config['config'];
$authManager = $container->get(AuthManager::class);
$authService = $container->get(\Zend\Authentication\AuthenticationService::class);
return new UserController($db_config, $authManager, $authService);
}
}
UserController.php
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\JsonModel;
use Zend\View\Model\ViewModel;
use Zend\Crypt\Password\Bcrypt;
class UserController extends AbstractActionController{
private $db;
private $authManager;
private $authService;
/**
* Constructor.
*/
public function __construct($config, $authManager, $authService){
$this->connect($config);
$this->authManager = $authManager;
$this->authService = $authService;
}
/**
* Connection.
*/
private function connect($config){
try{
if ( !class_exists('Mongo')){
echo ("The MongoDB PECL extension has not been installed or enabled");
return false;
}
$connection= new \MongoClient($config['connection_string'],
array('username'=>$config['username'],'password'=>$config['password']));
return $this->db = $connection->selectDB($config['dbname']);
}catch(Exception $e) {
return false;
}
}
}
When I run the service from browser it works, but if i execute the test file i get this error:
1) Api\Domain\Tests\ServiceTest::testSetUp
RuntimeException: A facade root has not been set.
/WWW/api/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:210
/WWW/api/src/Api/Infrastructure/Domain/Model/SolrBaseRepository.php:30
/WWW/api/src/Api/Infrastructure/Domain/Model/SolrBaseRepository.php:30
/WWW/api/src/Api/Domain/Tests/ServiceTest.php:80
File confguration: config/solr.php
return array(
'endpoint' => array(
'localhost' => array(
'host' => '192.168.1.100',
'port' => 8080,
'path' => '/solr/clients/',
)
)
);
Solr base repository:
use Illuminate\Support\Facades\Config;
abstract class SolrBaseRepository
protected $client;
/**
* SolrBaseRepository constructor.
*/
public function __construct(){
$this->client = new \Solarium\Client(Config::get('solr'));
}
}
if I change
$this->client = new \Solarium\Client(Config::get('solr'));
by
$this->client = new \Solarium\Client(array(
'endpoint' => array(
'localhost' => array(
'host' => '192.168.1.100',
'port' => 8080,
'path' => '/solr/clients/',
)
)
));
The test works.
ServiceTest: Api/Domain/Tests/ServiceTest.php
class ServiceTest extends \PHPUnit_Framework_TestCase{
public function testSetUp()
{
$this->setUp();
$this->solrServicesRepository = New SolrServicesRepository();
}
}
I have tried to resolve it adding from this page to my ServiceTest.php:
use \Illuminate\Container\Container as Container;
use \Illuminate\Support\Facades\Facade as Facade;
/**
* Setup a new app instance container
*
* #var Illuminate\Container\Container
*/
$app = new Container();
$app->singleton('app', 'Illuminate\Container\Container');
/**
* Set $app as FacadeApplication handler
*/
Facade::setFacadeApplication($app);
But this does not work for mi.
It's been a while, but I found the answer as I got stuck with the same problem.
In your test, you need to call the parent setup function, as it initializes:
class ExampleTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->faker = Faker\Factory::create();
// other lines of code that you need
}
public function testWithFacades()
{
//now it works
}
}
I am getting this error message:
The table "Permissionactions" for active record class
"Permissionactions" cannot be found in the database.
/var/www/html/YII/framework/db/ar/CActiveRecord.php(2310)
private $_model;
/**
* Constructor.
* #param CActiveRecord $model the model instance
*/
public function __construct($model)
{
$this->_model=$model;
$tableName=$model->tableName();
if(($table=$model->getDbConnection()->getSchema()->getTable($tableName))===null)
throw new CDbException(Yii::t('yii','The table "{table}" for active record class "{class}" cannot be found in the database.',
array('{class}'=>get_class($model),'{table}'=>$tableName)));
if($table->primaryKey===null)
{
$table->primaryKey=$model->primaryKey();
if(is_string($table->primaryKey) && isset($table->columns[$table->primaryKey]))
$table->columns[$table->primaryKey]->isPrimaryKey=true;
elseif(is_array($table->primaryKey))
{
foreach($table->primaryKey as $name)
{
if(isset($table->columns[$name]))
$table->columns[$name]->isPrimaryKey=true;
There is no any Permissionactions named table in my db. This is the main.php connection:
'db' => array(
'connectionString' => 'mysql:host=xxxxxxxxx;dbname=xxxxx',
'emulatePrepare' => true,
'username' => 'xxxxxxxx',
'password' => 'xxxxxxxxxxx',
'charset' => 'utf8',
),
'dbpermissions' => array(
'connectionString' => "mysql:host=xxxxxxxx;dbname=xxxxx",
'emulatePrepare' => true,
'username' => 'xxxxxxxx',
'password' => 'xxxxxxxxxxxxxxx',
'charset' => 'utf8',
'class' => 'CDbConnection'
),
Any idea why am I getting this ?
UPDATE:
This is the controller:
class MainController extends Controller {
public $model;
public function init(){
$this->model = Permissionactions::model();
}
...
and the Permissionsactions class (model):
class Permissionactions extends ActiveRecord{
private $connection_permission;
//private $connection_invetory;
/**
* #see db connections from config/main.php
*/
public function __construct(){
$this->connection_permission = Yii::app()->dbpermissions;
}
public static function model($className=__CLASS__){
return parent::model($className);
}
....
On my localhost it works. But now I put it on a server and it shows me this error.
Search thru your Code with your Editor/IDE function Search in Files / search in Project and locate the String
Permissionactions
Provide some more Information about your Issue and your Config.
I'd like to keep users away from editing configuration files, so I've made web interface in admin panel for setting up Mail server, username, password, port, encryption..
I was working well in Laravel 4.2, but now when the app has been rewritten into Laravel 5, an error occurs:
Class 'Settings' not found in <b>F:\htdocs\app\config\mail.php</b> on line <b>18</b><br />
For this purpose I've created a service provider, made a facade, put them in config/app.php, Settings::get('var')/Settings::set('var') work perfectly, but not for mail settings.
config/mail.php:
<?php return array(
'driver' => Settings::get('mail_driver'),
'host' => Settings::get('mail_host'),
'port' => Settings::get('mail_port'),
'from' => array('address' => Settings::get('mail_from_address'), 'name' => Settings::get('mail_from_name')),
'encryption' => Settings::get('mail_encryption'),
'username' => Settings::get('mail_username'),
'password' => Settings::get('mail_password'),
'sendmail' => Settings::get('mail_sendmail'),
'pretend' => false,
);
config/app.php:
'providers' => [
...
'App\Providers\SettingsServiceProvider',
...
'aliases' => [
...
'Settings' => 'App\Custom\Facades\Settings',
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Custom\Settings;
class SettingsServiceProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton('settings', function()
{
return new Settings;
});
}
}
<?php namespace App\Custom;
use App\Setting;
class Settings {
public function get($var) {
try{
$setting = Setting::first();
} catch(exception $e)
{
return false;
}
return $setting->$var;
}
public function set($var, $val) {
try{
$setting = Setting::first();
$setting->$var = $val;
$setting->save();
} catch(exception $e)
{
return false;
}
return true;
}
}
<?php
namespace App\Custom\Facades;
use Illuminate\Support\Facades\Facade;
class Settings extends Facade {
protected static function getFacadeAccessor() { return 'settings'; }
}
Any ideas how to implement Laravel mail settings using database?
To archive this I created CustomMailServiceProvider by extending Illuminate\Mail\MailServiceProvider so as to overwrite this method:
protected function registerSwiftTransport(){
$this->app['swift.transport'] = $this->app->share(function($app)
{
return new TransportManager($app);
});
}
Here is the complete solution
I created CustomMailServiceProvider.php in app\Providers
namespace App\Providers;
use Illuminate\Mail\MailServiceProvider;
use App\Customs\CustomTransportManager;
class CustomMailServiceProvider extends MailServiceProvider{
protected function registerSwiftTransport(){
$this->app['swift.transport'] = $this->app->share(function($app)
{
return new CustomTransportManager($app);
});
}
}
I created CustomTransportManager.php in app/customs directory -
NB: app/customs directory doesn't exist in default laravel 5 directory structure, I created it
namespace App\Customs;
use Illuminate\Mail\TransportManager;
use App\Models\Setting; //my models are located in app\models
class CustomTransportManager extends TransportManager {
/**
* Create a new manager instance.
*
* #param \Illuminate\Foundation\Application $app
* #return void
*/
public function __construct($app)
{
$this->app = $app;
if( $settings = Setting::all() ){
$this->app['config']['mail'] = [
'driver' => $settings->mail_driver,
'host' => $settings->mail_host,
'port' => $settings->mail_port,
'from' => [
'address' => $settings->mail_from_address,
'name' => $settings->mail_from_name
],
'encryption' => $settings->mail_encryption,
'username' => $settings->mail_username,
'password' => $settings->mail_password,
'sendmail' => $settings->mail_sendmail,
'pretend' => $settings->mail_pretend
];
}
}
}
And finally, I replaced 'Illuminate\Mail\MailServiceProvider', in config/app.php with 'App\Providers\CustomMailServiceProvider',
I have added
$this->app['config']['services'] = [
'mailgun' => [
'domain' => $settings->mailgun_domain,
'secret' => $settings->mailgun_secret,
]
];
to CustomTransportManager __construct() to include mailgun API credentials that I'm using as mailing service
I configured as mentioned, however got the following error. While I tried your code found that from Laravel 5.4 share method is deprecated and instead informed to use singleton.
Call to undefined method Illuminate\Foundation\Application::share()
here is the below method using singleton instead using share method:
protected function registerSwiftTransport(){
$this->app->singleton('swift.transport', function ($app){
return new CustomTransportManager($app);
});
}
#DigitLimit , method share() has been dropped since Laravel 5.4. I had to work-around this problem using other methods, and I am not sure they are perfect. Here is my registerSwiftTransport() method in CustomMailServiceProvider class.
Firstly, we need to determine if code is not executed while calling app through command line: "if(strpos(php_sapi_name(), 'cli') === false)". If we don't check that and don't prevent setting new params in this case, Artisan will throw us errors in command line. Secondly, we need to get settings from database somehow. I did it using my method getSettingValue(), where first argument is setting key, and second argument is default value if setting is not found. As you see, I assigned settings to $this->app['config']['mail'].
After that, I used singleton() method:
protected function registerSwiftTransport(){
if (strpos(php_sapi_name(), 'cli') === false) {
$this->app['config']['mail'] = [
'driver' => Setting::getSettingValue('mail_driver', '****'),
'host' => Setting::getSettingValue('mail_host', '****'),
'port' => Setting::getSettingValue('mail_port', 25),
'from' => [
'address' => Setting::getSettingValue('mail_from_address', '****'),
'name' => Setting::getSettingValue('mail_from_name', '****'),
],
'encryption' => Setting::getSettingValue('mail_encryption', '***'),
'username' => Setting::getSettingValue('mail_username', '****'),
'password' => Setting::getSettingValue('mail_password', '****'),
];
}
$this->app->singleton('swift.transport', function ($app) {
return new Illuminate\Mail\TransportManager($app);
});
}
I have problem with creating custom translator from database in ZF2. I have a DB like this
and files:
1)Application/module.config.php
'service_manager' => array(
'abstract_factories' => array(),
'factories' => array(
'translator' => function (\Zend\ServiceManager\ServiceManager $serviceManager)
{
$pluginManager = new \Zend\I18n\Translator\LoaderPluginManager();
$pluginManager->setServiceLocator($serviceManager);
$pluginManager->setFactory('DatabaseTranslationLoaderFactory', function($serviceManager)
{
$translator = new \Zend\I18n\Translator\DatabaseTranslationLoaderFactory();
return $translator->createService($serviceManager);
});
$translator = new \Zend\I18n\Translator\Translator(array());
$translator->setFallbackLocale('en_US');
$translator->setPluginManager($pluginManager);
$translator->addRemoteTranslations('DatabaseTranslationLoaderFactory');
return $translator;
},
),
),
'translator' => array(
'locale' => 'en_US',
'translation_file_patterns' => array(
array(
'type' => 'Zend\I18n\Translator\Loader\Database',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
),
),
),
2) Zend/I18n/Translator/Loader/Database.php
<?php
namespace Zend\I18n\Translator\Loader;
use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Sql;
use Zend\I18n\Translator\Plural\Rule as PluralRule;
use Zend\I18n\Translator\TextDomain;
class Database implements RemoteLoaderInterface {
protected $dbAdapter;
public $dbAdapter;
public function __construct(Adapter $dbAdapter = null)
{
if ($dbAdapter === null)
{
$configArray = array('driver' => 'Pdo_Mysql',
'database' => 'dbname',
'username' => 'username',
'password' => 'pswd',
'hostname' => 'localhost',
'charset' => 'utf-8',
);
$dbAdapter = new Adapter($configArray);
}
$this->dbAdapter = $dbAdapter;
}
public function load($locale, $textDomain)
{
$sql = new Sql($this->dbAdapter);
$select = $sql->select('ic_var')->columns(array('value'))
->where(array('language' => $locale, 'name' => $textDomain));
$messages = $this->dbAdapter->query(
$sql->getSqlStringForSqlObject($select),
Adapter::QUERY_MODE_EXECUTE
);
$textDomain = new TextDomain();
foreach ($messages as $message) {
if (isset($textDomain[$message['name']])) {
if (!is_array($textDomain[$message['name']])) {
$textDomain[$message['name']] = array(
$message['plural_index'] => $textDomain[$message['name']]
);
}
$textDomain[$message['name']][$message['plural_index']] = $message['value'];
} else {
$textDomain[$message['name']] = $message['value'];
}
}
return $textDomain;
}
}
3) Zend/I18n/Translator/DatabaseTranslationLoaderFactory.php
<?php
namespace Zend\I18n\Translator;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\I18n\Translator\Loader\Database;
class DatabaseTranslationLoaderFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new Database($serviceLocator->get('Zend\Db\Adapter\Adapter'));
}
}
4) Application/Module.php
public function onBootstrap(MvcEvent $e)
{
$translator = $e->getApplication()->getServiceManager()->get('translator');
$translator->addTranslationFile(
'DatabaseTranslationLoader',
'text-domain',
'text-domain'
);
}
But translation doesn`t work, because db adapter not find in loader:
Fatal error: Uncaught exception 'Zend\I18n\Exception\RuntimeException' with message 'Specified loader is not a file loader'
Thanks for your answers!
First of all you shouldn't define your custom classes in the Zend namespace as this is reserved a namespace for the ZF2 library and you don't want to touch (or add) files in the vendor directory.
Just put the custom classes in your own namespace outside the vendor folder. i.e. MyI18n
You can register you custom remote loader to the pluginManager in module.config.php.
return array(
'translator' => array(
'loaderpluginmanager' => array(
'factories' => array(
'database' => 'MyI18n\Translator\DatabaseTranslationLoaderFactory',
)
),
'remote_translation' => array(
array(
'type' => 'database' //This sets the database loader for the default textDomain
),
),
)
);
You don't have to overwrite the Translator factory if you want to add a custom loader, so just remove that code in your Module.php.
Als remove the configuration under translation_file_patterns as this is only needed for file loaders.
EDIT
For the above to work you need to overwrite the TranslatorServiceFactory because ZF has no support to register custom loaders on the plugin manager.
namespace MyNamespace\Translator;
use Zend\Mvc\Service\TranslatorServiceFactory as BaseTranslatorFactory;
class TranslatorServiceFactory extends BaseTranslatorFactory
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return MvcTranslator
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$translator = parent::createService($serviceLocator);
$config = $serviceLocator->get('Config');
$pluginManagerConfig = isset($config['translator']['loaderpluginmanager']) ? $config['translator']['loaderpluginmanager'] : array();
$pluginManager = new LoaderPluginManager(new Config($pluginManagerConfig));
$pluginManager->setServiceLocator($serviceLocator);
$translator->setPluginManager($pluginManager);
return $translator;
}
}
Now register your custom factory in the service configuration:
class Module
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MvcTranslator' => 'MyNamespace\Translator\TranslatorServiceFactory',
)
)
}
}
I register custom remote loader to the pluginManager in module.config.php like this
'translator' => [
'loaderpluginmanager' => [
'factories' => [
'database' => function($lpm){
$sm = $lpm->getServiceLocator();
$loader = new Zf2Translation\Loader\DatabaseTranslationLoader($sm);
return $loader;
},
],
],
'remote_translation' => [
[
'type' => 'database',
],
],
]
Next in Database Loader class
use Zend\I18n\Translator\Loader\RemoteLoaderInterface;
class DatabaseTranslationLoader implements RemoteLoaderInterface
{
protected $dbAdapter;
protected $sm;
public function __construct(ServiceManager $sm)
{
$this->sm = $sm;
$this->dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
}
}
I hope it helps.