How to use cakephp 2 with custom database connection and raw queries - cakephp

I have to work with an Oracle database using the old database driver (ora_logon ) which is not supported by cakephp. I cant use the oci driver instead.
Right now I do the follow:
Every method of every model connects to the database and retrieve data
class SomeClass extends Model {
public function getA(){
if ($conn=ora_logon("username","password"){
//make the query
// retrieve data
//put data in array and return the array
}
}
public function getB(){
if ($conn=ora_logon("username","password"){
//make the query
// retrieve data
//put data in array and return the array
}
}
}
I know that it is not the best way go.
How could I leave cakephp manage opening and closing of the connection to the database and have models only retrieve data? I'm not interested in any database abstraction layer.

I would think you could just make your own OracleBehavior. Each model could use this behavior, and in it, you can overwrite or extend the Model's find() behavior to build a traditional oracle query and run it (I don't know much about Oracle).
Then, in your Behavior's beforeFind() you can open your connection, and in your Behavior's afterFind(), you can close your database connection.
That way, every time before a query is run, it automatically opens the connection, and every time after a find it closes it. You can do the same with beforeSave() and afterSave() and beforeDelete() and afterDelete(). (You'll likely want to create a single connect() method and disconnect() method in the Behavior, so you don't have duplicate code in each beforeX() method.

Do you really need to extend a Cake Model class?
class SomeClass extends Model {
private $conn;
public function constructor() {
parent::constructor();
$conn = ora_logon("username","password");
if(!$conn)
throw new Exception();
}
public function getA() {
//Some code
}
}
SomeController:
App::uses('SomeClass','Model');
public function action() {
$data = array();
$error = null;
try{
$myDb = new SomeClass();
$data = $myDb->getA();
} catch($e) {
$error = 'Cannot connect to database';
}
$this->set(compact('data', 'error'));
}

Related

How to load configuration after login in symfony

I have a Symfony 3.4 app that could contain multiple companies.
Each company have their own config, and their own data in db, so I need that each company have their own db.
When any user login, The application has a "core database" containing user's info.
After user login the application must change configuration for connect to user company database, that had saved in "core database".
There are necessary steps:
One user enter his user and password
the app look into central db and get user's authentication.
The app get user configuration to change.
The app change the configuration and now, sql request will be to the company's db.
It is possible? If not, is there any alternative?
Thank you so much!
You have to work here with multiple entity managers and connections and and idea is to use a subscriber that retrieves the current customer based on the user. This subscriber (or another service) will set a global variable containing the name of the entity manager.
// A subscriber (high level priority) or a service already set $customerName
// In your controller or in a service
$customerEntityManager = $this->getDoctrine()->getManager($customerName);
Check also this bundle for ideas https://github.com/vmeretail/multi-tenancy-bundle
Edit
Use and adapt to your needs this file https://github.com/vmeretail/multi-tenancy-bundle/blob/master/Service/TenantResolver.php
Here you just need to resolve tenant from the current User.
In your controller:
...
public function index(TenantResolver $tenantResolver)
{
$customerEntityManager = $this->getDoctrine()->getManager($tenantResolver->getTenant()->getName()); // or getId() or something else
}
In a service:
...
use Doctrine\Common\Persistence\ManagerRegistry;
private $tenantResolver;
private $managerRegistry;
public function__construct(TenantResolver $tenantResolver, ManagerRegistry $managerRegistry)
{
$this->tenantResolver = $tenantResolver;
$this->managerRegistry = $managerRegistry;
}
public function doSomething()
{
$this->managerRegistry->getManager($this->tenantResolver->getTenant()->getName()); // or getId() or something else
}
It's the idea, there must be something better to do here like injecting directly the right manager in the service/controller constructor.
I found the following solution for Symfony 4 and i think it should work for symfony 3.4 as well.
I created a service that copies the default entity manager in a new one connecting to another database:
namespace App\Service;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Doctrine\ORM\EntityManagerInterface;
class CustomEntityManagerHelper
{
private $em;
public function __construct(EntityManagerInterface $entityManager)
{
$this->em = $entityManager;
}
/*
* get entity manager for another database
*/
public function getManagerForDatabase($db_name): EntityManagerInterface
{
$conn = array(
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => 'mypass',
'dbname' => $db_name
);
return \Doctrine\ORM\EntityManager::create(
$conn,
$this->em->getConfiguration(),
$this->em->getEventManager()
);
}
}
Until now it was very easy but the Repository class still uses the default entitymanager. So i added a method setEntityManager to the Repositories:
<?php
namespace App\Repository;
use App\Entity\Product;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityManagerInterface;
class ProductRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Product::class);
}
public function setEntityManager(EntityManagerInterface $entityManager): self
{
$this->_em = $entityManager;
return $this;
}
// custom methods here
}
Now i can use the custom entity manager AND set that to the repository:
use App\Service\CustomEntityManagerHelper;
// ...
/**
* #Route("/products", name="app_product", methods={"GET"})
*/
public function index(CustomEntityManagerHelper $helper): Response
{
$myManager = $helper->getManagerForDatabase($this->getUser()->getDatabaseName());
$products = $myManager->getRepository('App:Product')
->setEntityManager($myManager) // IMPORTANT!
->findAll();
return $this->render('product/index.html.twig', [
'products' => $products
]);
}

Can I initialize data map in open function of ScalarFunction in Flink

I implemented a customized ScalarFunction class, I would like to initialize a hashmap which read data from a database in open() function, but it always stuck over there, so is it a right way to use open() function like this? and how many time the open() function would be invoked, just once or the same times with eval() function?
My sample code as following:
public class GenNameUDF extends ScalarFunction {
#Override
public void open(FunctionContext context) throws Exception {
super.open(context);
CommonClass.map = initMap();//here will read data from db
}
public String eval(String pubIp) {
return todo();
}
}
If the amount of data in db is small, you can use broadcast to acess db only once
Otherwise, you can build connection in open method, and access db when needed
-yt $file is also helpful.

Migrate a laravel project with a single database to multiple bases

I would have to make many modifications to be able to transfer my laravel project with a single database that has many query builders and eloquent to a project that supports more than one database?
I understand that once a new database is installed it is necessary to use:
connection('mysql2')
When consulting a database, do we tend to change the whole project with this sentence? specifying the connection in each place?
You can add a $connection property to your Eloquent models to specify the database connection there. This way you don't need to update your queries.
protected $connection = 'connection-name';
Migration with multiple connections
public function up()
{
Schema::connection('mysql-2')->create('user_details', function (Blueprint $table) {
//........
});
}
public function down()
{
Schema::connection('mysql-2')->dropIfExists('user_details');
}
Handle Relationship with multiple database connections
UserDetail.php //mysql-2 (connection-2)
class UserDetail extends Model
{
protected $connection = 'mysql-2';
public function user()
{
return $this->setConnection('mysql')
->belongsTo(User::class);
}
}
User.php //mysql (connection-1) //default connection
class User extends Model
{
//with default connection
public function detail()
{
return $this->setConnection('mysql-2')
->hasOne(UserDetail::class);
}
}
You don't need to a specified connection in a controller for retrieving/delete/insert data
I Understand that you are asking basically how to change database.
For whole project: you can edit mysql connection details in your .env file.
You can also use 2 databases with 1 project , you can learn how to do that from this question which has been already answered: How to use multiple databases in Laravel
I am sorry if i didn't understand your question.
Let me know if it helps you.

Output data from database in JSF page

I am making a project using JSF, and I know how to get data from my view. I also know how to get data with the JDBC connector. And also how to put data in the view, from some objects, but my question is:
How to put data directly from my database, for example a list of person, in JSF, for example with the tag <h:outputText value="#{}"/> ?
I have found some examples with instantiate objects, but I did not found a real example with data from a DB.
JSF is just an MVC framework to develop web applications in Java. JSF doesn't associate with any data source at all. The only data JSF will use is retrieved from:
The data already stored in the proper object as attribute: HttpServletRequest, HttpSession or ServletContext.
The request/view/session/application context in form of fields in the managed beans, recognized by classes decorated as #ManagedBeans or #Named if using CDI. The data of these fields will be stored as attributes in the objects mentioned in the section above, depending on the scope of the managed bean.
By knowing this, then the only thing you should worry about is to fill the fields in your managed beans. You can fill them with incoming data from database, from a web service or whatever data source you have in mind.
For example, if you want/need to populate your data to pre process a request, you can do the following:
#ManagedBean
#ViewScoped
public class SomeBean {
List<Entity> entityList;
#PostConstruct
public void init() {
SomeService someService = new SomeService();
entityList = someService.findEntityList();
}
//getters and setters for the list...
}
//as you can see, this class is just pure Java
//you may use other frameworks if you want/need
public class SomeService {
public List<Entity> findEntityList() {
String sql = "SELECT field1, field2... FROM table";
List<Entity> entityList = new ArrayList<>();
try (Connection con = ...; //retrieve your connection somehow
PreparedStatement pstmt = con.prepareStatement(sql)) {
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
Entity entity = new Entity();
entity.setField1(rs.getString("field1"));
entity.setField2(rs.getString("field2"));
//...
entityList.add(entity);
}
} catch (Exception e) {
//handle exception ...
e.printStackTrace();
}
return entityList;
}
}

CakePHP Logging to Live DB During Unit Testing

I'm using DB logging in Cake 2.1, which works great.
The problem I'm having is when running Unit Tests, all logs are still getting sent to the live db rather than the test database.
All other db interactions go to test, except logging.
I do have a log fixture created and imported into the test case.
Here's my Database logger (/Lib/Log/Engine/DatabaseLogger.php)
App::uses('CakeLogInterface', 'Log');
class DatabaseLogger implements CakeLogInterface
{
public function __construct($options = array() )
{
App::import('Model', 'Log');
$this->Log = new Log;
}
public function write($type, $message)
{
$this->Log->create();
$log['type'] = ucfirst($type);
$log['date'] = date('Y-m-d H:i:s');
$log['message'] = $message;
return $this->Log->save($log);
}
}
I'm sure I'm missing some basic setting here but I can't figure this out for the life of me.
Well, in my case the problem was caused because of a bad initialization of a constructor.
You can check the update solution here:
How to choose the test DB cakePHP testing
And here:
How to override model's constructor correctly in CakePHP

Resources