Multiple concurrent database connections in drupal 7 - drupal

I'm writing a wrapper class for my drupal 7 site which lets me connect to and query my phpbb database.
When connecting to an external data source (as per drupal documentation) you have set the active db, run the query, then set the active db back to the default.
e.g.
db_set_active('phpbb');
$result = db_query($sql,$args,$opts);
db_set_active();//back to default
But is there any way to use drupal's database wrapper to create a brand new connection which can be permanently set to the new database without having to do this switching back-and-forth nonsense? surely we can handle connections to multiple databases concurrently.
I have done some googling but haven't found anybody trying to do this as yet.

Typical. 5 minutes after posting i figure it out... so, for future googlers:
Basically, you don't use db_query, instead you run the query on your connection without setting the active link.
you can figure this out by looking at how db_query works:
http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_query/7
So it looks like this:
$target='default';
$key = 'phpbb';
$phpbb = Database::getConnection($target,$key);
$result = $phpbb->query($sql,$args,$opts);
This assumes you have a database configured in your settings.php like the following:
$databases['phpbb']['default'] = array(
'driver' => 'mysql',
'database' => 'forum',
'username' => 'username',
'password' => 'password',
'host' => 'mysql.host.com',
'prefix' => 'phpbb3_'
);

Database::addConnectionInfo() perhaps?
This method allows the addition of new connection credentials at
runtime. Under normal circumstances the preferred way to specify
database credentials is via settings.php. However, this method allows
them to be added at arbitrary times, such as during unit tests, when
connecting to admin-defined third party databases, etc.
If the given key/target pair already exists, this method will be
ignored.

The definition for getConnection cites a different order for arguments than used above.
function getConnection($target = 'default', $key = NULL)
This is sadly different from Database::addConnectionInfo() which is
public static function addConnectionInfo($key, $target, $info)
Also, on DB_select, the $key is not a parameter, though it is in the options array:
function db_select($table, $alias = NULL, array $options = array()) {
if (empty($options['target'])) {
$options['target'] = 'default';
}
return Database::getConnection($options['target'])->select($table, $alias, $options);
}
while
final public static function getConnection($target = 'default', $key = NULL) {
so this implies that the 'master' or 'slave' or 'default' is always used as set, but not the key to the alternative database/schema, requiring the db_set_active('...'); and db_set_active(); around the db_select.
Since calls to other dbs can easily be required within the processing of the db_select (such as devel calls or calls in alters), this is inflexible design. Changing this call:
return Database::getConnection($options['target'])->select($table, $alias, $options);
to add the Key parameter (it is already spec'd as an argument!!) is needed but insufficient so far as I can now see.

Related

bbpress user-roles or capabilities per forum

I'm trying to setup a bbpress with extended user capabilities.
The problem
My goal is that users need to have different capabilities in each forum, i.e:
UserA can't access ForumW
UserA can only read topics and replies in ForumX
UserA can create topics and write replies in ForumY
UserA can moderate ForumZ
Plugins
These are the plugins I tried so far, but without success:
Ultimate Member, official 1.7 and the new 2.0 version
https://ultimatemember.com/
They claim that they're working on a groups extension for UltimateMember v2, which somehow looks promising, but as of now there's no release date and I still don't know if this extension is going to solve my problem.
itthinx Groups plugin
http://docs.itthinx.com/document/groups/
Allows me to assign multiple groups to users and forums, but there's still a catch.
First attempt
Since itthinx Groups plugin allows me to assign multiple groups to UserA, which is great, it's still not solving my issue.
So, I tried something like this:
ForumX has the following groups assigned: ForumX_readers, ForumX_writers, ForumX_moderators
UserA has the following groups assigned: ForumX_readers, ForumY_writers, ForumZ_moderators
But the problem is, since UserA belongs to groups that have publish_replies and moderate capabilities, he has full access to ForumX.
So what I need is an intersection of the forum-groups and the user-groups - which in this example is ForumX_readers.
The promising part, but...
I digged into the code of the plugin and found the line that handles the capabilities of the user based on his assigned groups and quickly tried to get the current forum groups, to implement the intersection.
Unfortunatelly I was not able to access the global $post, the $_GLOBALS['post'] nor the $_REQUEST[] variables in this part of code. Neither directly nor with an apply_filters() function, that I implemented into the part of the code myself.
UPDATE:
I was able to get the ID with get_posts() and the slug of the current forum/topic.
So, my question
Is there any solution to my first attempt, which I may have overseen?
If not, is there maybe any other plugin that can solve my problem that I'm not aware of?
Or is something like that even impossible in bbpress?
After some further research and trial & error, I finally figured it out.
First step to do is to set up the capabilities, which in my case look something like this.
In the plugins directory, there is the file core/class-groups-user.php. The init_cache() function retrieves the assigned groups to the user, and sets the according capabilities.
To not mess around to much with the core-plugin, I applied a filter to the $group_ids variable which can be found in line: 415.
foreach( $user_groups as $user_group ) {
$group_ids[] = Groups_Utility::id( $user_group->group_id );
}
// added this line
$group_ids = apply_filters('filter_user_group_ids', $group_ids);`
I then created a new plugin, which hooks into this filter.
add_filter('filter_user_group_ids', 'dnmc_filter_groups', 10, 1);
function dnmc_filter_groups($user_group_ids) {
$forum_id = dnmc_get_forum_id();
if(!$forum_id) return $user_group_ids;
$forum_group_ids = Groups_Post_Access::get_read_group_ids( $forum_id);
$user_restricted_forum_group_ids = array_intersect($user_group_ids, $forum_group_ids);
return $user_restricted_forum_group_ids;
}
function dnmc_get_forum_id() {
$args_topic = array(
'name' => basename( untrailingslashit( rtrim($_SERVER['REQUEST_URI'], '/') ) ),
'post_type' => 'topic',
'post_status' => 'publish',
'numberposts' => 1
);
if($topic = get_posts($args_topic)) {
return $topic[0]->post_parent;
}
$args_forum = array(
'name' => basename( untrailingslashit( rtrim($_SERVER['REQUEST_URI'], '/') ) ),
'post_type' => 'forum',
'post_status' => 'publish',
'numberposts' => 1
);
if($forum = get_posts($args_forum)) {
return $forum[0]->ID;
}
return false;
}

Strange Doctrine 2 behaviour

I am using a Symfony command to retrieve member details from an API afor updating in a local database.
I query my local database and then loop through each member to retrieve data and update individually. However, doctrine is not updating these member details.
I have tried to use persist and merge but none of them seems to be working which is really baffling. Below is the code I am using, hopefully an extra pair of eyes will spot what I am not:
$members = $em->getRepository('AppBundle:Member')->findByNot(array('pin' => "'NULL'"),array('updated_at' => 'ASC'),4,0);
foreach($members as $member)
{
$details = json_decode($api_url->apiConnect(array('login' => $member->getId(), 'password' => $member->getPassword())), true);
$member->setFirstName($details['firstName']);
$member->setLastName($details['lastName']);
$member->setCard($details['card']);
//$em->merge($member);
$em->persist($member);
$em->flush();
}
When I check the mysql query log, none of the update queries are run.

Symfony2 get validation message

I have a few validation messages in the file validators.en.yml:
soporte.nombre.not_blank: The name cannot be empty
soporte.price.is_integer: The price should be integer
soporte.not_repeat: soporte cannot be repeat
I valid making a query to the database:
$validarPorNombreAndTipo = $this->crud->findOneBy(
$soporte, array('nombre' => $soporte->getNombre(),
'tipo' => $object->tipo
)
);
if ($validarPorNombreAndTipo){
$error= //here i need to get soporte.not_repeat that is on file validators.en.yml;
}
excuse me for my bad English, I used the translator.
If you just want to get the translation, this code should work :
$validarPorNombreAndTipo = $this->crud->findOneBy(
$soporte, array('nombre' => $soporte->getNombre(),
'tipo' => $object->tipo
)
);
if ($validarPorNombreAndTipo){
$error= $this->get('translator')->trans('soporte.not_repeat', array(), 'validators');
// Third param is the translation domain (first part of the translation file)
}
However, i suggest you to read this : http://symfony.com/doc/2.3/cookbook/validation/custom_constraint.html
If you want to follow Symfony best practices, you should create a custom constraint class/Validator and move your validation logic into it.
Best regards

How WordPress can list users with specific capabilities

Is there a way to list only the users that has a specific capability, such us "publish_posts" ?
To select users with certain capabilities you can use WP_User_Query with meta_query parameter, because WP stores capabilities as a serialized string in user_meta table.
Also remember that due to availability to have multisite installation capabilities name in user meta looks like wp_table_prefix_capabilities.
global $wpdb;
// meta-key name
$capabilities_field_name=$wpdb->prefix.'capabilities';
//array as argument for our query
$qargs=[
'role' => ['Customer'], // use this if you need to query by role at the same time
'meta_query'=>
[
'relation' => 'OR', // optional if you'll need to select more than
// one capability just add this and create same array
// as down below describing what are you looking for
[
'key' => $capabilities_field_name,
'value' => 'your_role_name',
'compare' => 'LIKE',
],
// here could be same array [key,value,compare]... as above with another capability
// but you'll need to add extra argument showing relationship between them see above 'relation parameter'
],
'number'=> -1 // to select all users
];
$usersQuery=new WP_User_Query($qargs); // instantiate UserQuery with $qargs
$users=$usersQuery->get_results(); // get all results as array of WPUser objects
Hope it helps somebody:)
Note [vars] could be substituted to array(vars), I like [] short syntax but it's supported only since php 5.4.
You can just retrieve all users. Then loop through them in a foreach. Check if the user has a specific capability then push the users to another array and use that array to list them.
$all_users = get_users();
$specific_users = array();
foreach($all_users as $user){
if($user->has_cap('specific_capability')){
$specific_users[] = $user;
}
}
NOTE:
It seemed a nice quick and dirty solution at the time, but now I would recommend writing a query. I do not have the time to investigate this for you, so if the one downvoting this would be so kind to answer this question instead of downvoting an answer which was an actual help to the inquirer, that would be nice.
You can list users with WP_User_Query, but afaik you can only return different roles, not permissions, maybe that's already what you want! There's also a site where you can see the different roles in the wordpress documentation.
You will first need to get all the roles that contain that capability. Then you can search users based on the roles that contain that capability.
$roles = array();
foreach ( wp_roles()->roles as $role_name => $role_obj ) {
if ( ! empty( $role_obj['capabilities']['my_capability_name'] ) ) {
$roles[] = $role_name;
}
}
$users = get_users( array( 'role__in' => $roles ) );
This does not account for if another role has "Deny" on that capability and your users can contain multiple roles. If so then you will also need to add a "user_can()" condition when looping through your Users. https://developer.wordpress.org/reference/functions/user_can/

How to connect to multiple databases within Drupal

I want to deploy openpublish on about 5 drupal multisite by high traffic on website and want to use multiple database server but some tables should be shared.
How to connect to multiple databases within Drupal by making share on 'users', 'sessions' and 'role' tables.
To allow multiple database connections, convert $db_url to an array.
<?php
$db_url['default'] = 'mysql://drupal:drupal#localhost/drupal';
$db_url['mydb'] = 'mysql://user:pwd#localhost/anotherdb';
$db_url['db3'] = 'mysql://user:pwd#localhost/yetanotherdb';
?>
To query a different database, simply set it as active by referencing the key name.
<?php
db_set_active('mydb');
db_query('SELECT * FROM table_in_anotherdb');
//Switch back to the default connection when finished.
db_set_active('default');
?>
But make sure all databases are of same kind.
The documentation on sharing tables says that you have to use a single database to share tables, but I've done it with multiple databases on the same server. I've done this by putting the database name before the prefix then adding a dot. So if your default database is called 'drupal', and your second database is called 'second_drupal', the prefixes would look like this in settings.php:
$db_prefix = array(
"default" => "slave1_", // the prefix for tables that are not shared.
"users" => "second_drupal.master_",
...
(Note that it doesn't matter what you call your default database, since it's the default database, you don't need to refer to it by name in the $db_prefix variable, as long as it's setup correctly in the settings.php file.)
http://thedrupalblog.com/setting-multi-site-drupal-6-installation-shared-databases-and-single-sign has some instructions, but I think that still assumes a single database server.
Sum times i wanted to retrieve data from other database in drupal then, i created a function in selected theme folder inside template.php file
connect_to_database($un, $pass, $db, $insert)
it takes four parameters of the database username, password, databasename, and last is query
function connect_to_database($username,$password,$database,$query){
$database_info = array(
'host' => 'hosname',
'database' => $database,
'username' => $username,
'password' => $password,
'driver' => 'mysql'
);
Database::addConnectionInfo('coreapp', 'default', $database_info);
db_set_active('newcon');
$q = db_query($query);
db_set_active('default');
return $q;
}

Resources