Trouble joining OrientDb Documents - sql

I have two OrientDb generic classes defined thus :
create class UserRole;
create property UserRole.name string (mandatory true, notnull true);
create property UserRole.permissions embeddedset (mandatory true, notnull true);
create class User;
create property User.email string (mandatory true, notnull true);
create property User.password string (mandatory true, notnull true);
create property User.role link UserRole (mandatory true, notnull true);
with the following example data:
insert into UserRole set name = 'Admin', permissions = ['users:delete', 'users:write', 'users:read'] // yields #10:0
insert into UserRole set name = 'User', permissions = ['users:write', 'users:read'] // yields #11:0
insert into User set email = 'admin#test.com', password = 'admin_password', role = #10:0
insert into User set email = 'bob#test.com', password = 'bob_password', role = #11:0
insert into User set email = 'dave#test.com', password = 'dave_password', role = #11:0
How can I retrieve all UserRole with a count of the number of User assigned to each ?
On a standard relational db I'd just do a JOIN with a GROUP BY but JOINs aren't an option with OrientDb...
I could also do it thus if I used Vertices but I'd like to know how to do it with generic documents
select *, in("ERole").size() from UserRole
Cheers

Related

Phalcon: save reference to new record in one transaction

Im trying to save a reference to a new object in a single transaction as shown here in the documentation under 'implicit transactions':
I am creating two new objects of the same class, one is then referencing the other. From the documentation, the save should be performed on TreeNodeA when TreeNodeB is saved and the ID will be passed to TreeNodeB->parent_tree_node_id
This doesnt seem to be working, and it still being passed as an object as Im getting the error on the save function:
Object of class TreeNodes could not be converted to string
I've tried writing a saveTreeParentNodeId function in the model and also setting it using the alias, but neither seem to work.
$treeNode = new TreeNodes();
$treeNode->setConnectionService(Registry::setConnection(MyModel::MAIN_DB));
$parentNode = $treeNode->findFirst();
$treeNodeA = new TreeNodes();
$treeNodeA->tree_id = $parentNode->tree_id;
$treeNodeA->tree_parent_node_id = $parentNode;
$treeNodeA->tree_level_id = 2;
$treeNodeA->node_desc = "Test Node A";
$treeNodeB = new TreeNodes();
$treeNodeB->tree_id = $parentNode->tree_id;
$treeNodeB->tree_parent_node_id = $treeNodeA;
$treeNodeB->tree_level_id = 3;
$treeNodeB->tree_desc = "Test Node B";
$treeNodeB->save();
The model:
class TreeNodes extends MyModel
{
public $node_id;
public $tree_id;
public $tree_parent_node_id;
public $tree_level_id;
public $node_desc;
public function getSource()
{
return "TreeNodes";
}
public function setTreeParentNodeId(TreeNodes $parentNode){
$this->tree_parent_node_id = $parentNode->node_id;
}
public function initialize()
{
parent::initialize();
$this->belongsTo(
'tree_id',
'Organisations',
'TreeID',
array(
'alias' => 'organisation',
'reusable' => true
)
);
$this->hasOne(
'tree_id',
'TreeType',
'tree_id',
array(
'alias' => 'type',
'reusable' => true
)
);
$this->hasOne(
'tree_parent_node_id',
'TreeNodes',
'node_id',
array(
'alias' => 'parentNode'
)
);
}
}
Update
By updating the model to use belongsTo, Phalcon recognises the parentNode.
$this->belongsTo(
'tree_parent_node_id',
'TreeNodes',
'node_id',
array(
'alias' => 'parentNode'
)
);
This enables $treeNodeA to save implicitly when $treeNodeB is saved.
$treeNodeA->parentNode = $parentNode;
Unfortunately, $treeNodeB with a reference to $treeNodeA as the parentNode is NOT saved. No error message is returned either, just 'true'.
In the documentation example you linked, they assign the $robotPart object to $robot->robotPart. robotPart refers to the linked RobotPart object and not to the ID ( to which you are trying to assign your object )
$treeNodeB = new TreeNodes();
$treeNodeB->tree_id = $parentNode->tree_id;
// $treeNodeB->tree_parent_node_id = $treeNodeA;
$treeNodeB->parentNode = $treeNodeA;
$treeNodeB->save();
You should use parentNode here because this is the name you gave to your relationship via hasOne.
I haven't tested this myself, but by following the documentation's logic, this should push you in the right direction.
Alter you model relationships so you have both sides of the relation
// let Phalcon know that "tree_parent_node_id" is a reference to "node_id"
$this->hasOne(
'tree_parent_node_id', // your column
'TreeNodes', // referenced table
'node_id', // referenced table column
array(
'alias' => 'parentNode',
'foreignKey' => true
)
);
// let Phalcon know that "node_id" is being referenced as a FK in "TreeNodes"
$this->belongsTo(
'node_id', // PK
'TreeNodes', // referenced table
'tree_parent_node_id', // referenced table column
array('foreignKey' => ['message' => 'FK constraint error between node_id and tree_parent_node_id'])
);

Get more logged-in information in Zend Framework2

I'm implementing a log-in form by using Zend Framework 2.
I have a "user" table in mysql database: user(user_id INT AUTO_INCREMENT,email-address, password).
The user will input email-address and password.
My web application will call authenticate() method with the identity is email-address, the credential is password.
If the validation success, getIdentity() return the email-address.
But not only the email-address, I want to know the user_id field to use for other query after logged-in.
How can I get more information in my "user" table?
I don't want to query from database twice. (once for authentication, once for user_id).
Do you have any suggestion?
Thanks.
Just authenticate the correct user call the AuthenticationService and the method getStorage with will return you the storage container for the current user. just pass a object or your user entity in this and after calling $this->identity() you should become your custom identity object.
$authService = $this->getServiceManager('Zend\Authentication\AuthenticationService');
if ( // user auth success ) {
$userObject = array(
'foo' => 'bar',
'baz' => 'bas'
);
$authService->getStorage()->write( $userObject );
}
now by calling $this->identity() u get the following output
var_dump( $this->identity() );
//output
array(
'foo' => 'bar',
'baz' => 'bas'
)
Sometime like this
// Set the input credential values (e.g., from a login form)
$authAdapter
->setIdentity('my_username')
->setCredential('my_password')
// Print the result row
print_r($authAdapter->getResultRowObject());
/* Output:
user table
Array
(
[user_id] => 1
[email-address] => my_username
[password] => my_password
)
Also read more manual: http://framework.zend.com/manual/current/en/modules/zend.authentication.adapter.dbtable.html

Assign groups based on logged user credentials

I'm developing a application using sfDoctrineGuardPlugin and Symfony 1.4.20 then I've three users and three users groups and some permissions as follow:
user_name user_group permissions
u1 Group1 can_admin_full, can_admin
u2 Group2 can_admin
u3 Group3 no_admin
So u1 should be able to add users to the application but only can see Group2 and Group3 under Groups Options, u2 should be able to add users to the application to but only can see Group3 under Groups Options, so u1 and u2 shouldn't add users belonging to Group1, how I can get this using sfDoctrineGuard? It's possible?
NOTE: I use GroupN as a example but below in the code sample is the right names for groups and permissions!
EDIT: Improve question
So after take a closer look at this I'm trying to to the same but adapted to my code so in lib/form/doctrine/sfDoctrineGuardPlugin/sfGuardUserForm.class.php I change this:
class sfGuardUserForm extends PluginsfGuardUserForm {
public function configure() {
//groups_list
$this->getWidget('groups_list')->setOption('expanded', true);
$this->getWidget('groups_list')->setOption('table_method', 'getListForAdmin');
$this->getValidator('groups_list')->setOption('query', Doctrine::getTable('sfGuardGroup')->getListForAdmin());
}
}
Also I made this changes at lib/model/doctrine/sfDoctrineGuardPlugin/sfGuardGroupTable.class.php
class sfGuardGroupTable extends PluginsfGuardGroupTable {
/**
* Returns an instance of this class.
*
* #return object sfGuardGroupTable
*/
public static function getInstance() {
return Doctrine_Core::getTable('sfGuardGroup');
}
/**
* Builds list query based on credentials
*
*/
public function getListForAdmin() {
$user = sfContext::getInstance()->getUser();
$q = $this->createQuery('g');
if ($user->hasPermissions('can_admin_full')) {
$q->addWhere('g.name IN (?)', array('Administradores Monitor', 'Monitor'));
} else if ($user->hasPermissions('can_admin')) {
$q->addWhere('g.name IN (?)', array('Monitor'));
}
return $q;
}
}
But don't work because login using a user that belongs to group "Administrador de Servicios" and has permissions 'can_admin' and 'can_admin_full' and I can see all the groups in the widget and I'm looking just for see in that case 'Administradores Monitor' and 'Monitor'
EDIT 2
Also try this other code:
$this->widgetSchema['groups_list'] = new sfWidgetFormDoctrineChoice(array('multiple' => true, 'table_method' => 'getListForAdmin', 'query' => Doctrine::getTable('sfGuardGroup')->getListForAdmin()));
And still not working, I change 'table_method' => 'getListForAdmin' to 'table_method' => 'getListForAdmin1' and nothing happens so I suspect that the method is never called
EDIT 3
Now it's working but see this:
If I use this approach:
$this->getWidget('groups_list')->setOption('expanded', true);
$this->getWidget('groups_list')->setOption('table_method', 'getListForAdmin');
$this->getValidator('groups_list')->setOption('query', Doctrine::getTable('sfGuardGroup')->getListForAdmin());
Then I get this error:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables
does not match number of tokens
If I use the other approach:
$this->widgetSchema['groups_list'] = new sfWidgetFormDoctrineChoice(array('multiple' => true, 'table_method' => 'getListForAdmin', 'query' => Doctrine::getTable('sfGuardGroup')->getListForAdmin()));
I get this other error:
sfWidgetFormDoctrineChoice requires the following options: 'model'.
Then I added the parameter model:
$this->widgetSchema['groups_list'] = new sfWidgetFormDoctrineChoice(array('multiple' => true, 'model' => 'sfGuardGroup', 'table_method' => 'getListForAdmin', 'query' => Doctrine::getTable('sfGuardGroup')->getListForAdmin()));
But get the same error as first approach, I suspect the problem is in getListForAdmin() function but really don't know where exactly
What's wrong at this point?
Try to change the conditional in getListForAdmin():
if ($user->hasPermissions('can_admin_full')) {
$q->whereIn('g.name', array('Administradores Monitor', 'Monitor'));
} else if ($user->hasPermissions('can_admin')) {
$q->whereIn('g.name', array('Monitor'));
}

Fluent API Many to Many Mapping Error

I have the following mapping to support a many to many table (User_Role)
modelBuilder.Entity<Role>()
.HasMany<User>(u => u.users)
.WithMany(r => r.roles)
.Map(m =>
m.MapLeftKey("role_id")
m.MapRightKey("user_id")
m.ToTable("User_Role"));
This works great from mapping many roles to a user and many users to a role. The problem I am having is when I need to add a new row to the table User_Role via an entity as follows:
public class User_Role
{
[Key Column(Order=1)]
public int role_id {get;set;)
[Key Column(Order=1)]
public int user_id {get;set;)
}
Whenever I try to access this entity as follows:
dbContext.User_Role.Add(new User_Role {user_id ....
EF looks for a non existent table called User_Role1 ... its adding a '1' to the table name.
I then tried to add:
[Table("User_Role")]
This takes care of the adding a '1' but I now get this error:
"The EntitySet 'RoleUser' with schema 'dbo' and table 'User_Role' was already defined. Each EntitySet must refer to a unique schema and table"
I was able to confirm that the following lines together are causing the problem but I kind of need them both
m.ToTable("User_Role") and public class User_Role..
Any suggestions would be great...I am stumped.
You cannot represent the join table in a many-to-many relationship by an entity class in your model. This join table is managed by EF and you cannot directly access this table. If you want to create a relationship between an existing user and an existing role you must do this using these two entities:
var user = dbContext.Users.Single(u => u.id == user_id);
var role = dbContext.Roles.Single(r => r.id == role_id);
// if you don't have lazy loading and don't instantiate the collection
// in the constructor, add this: user.roles = new List<Role>();
user.roles.Add(role);
dbContext.SaveChanges();
This will write an entry for the new relationship (the (user_id,role_id) pair) into the join table.
Edit
If you only have the two key properties at hand and don't want to load the user and role from the database you can work with attached "stub entities":
var user = new User { id = user_id, roles = new List<Role>() };
var role = new Role { id = role_id };
dbContext.Users.Attach(user);
dbContext.Roles.Attach(role);
user.roles.Add(role);
dbContext.SaveChanges();

Can LINQ2SQL return a few columns or is it entity or single column only?

From what I understand, using something like nHibernate will return either a single result like int (say returning a count) but it can't return say 2 columns or 3 columns from the users table.
Is linq the same way?
e.g. say I have the Users table with columns: UserID, username, password, email, datecreated)
Could it return UserID, password ONLY, or it can only return the entire row/entity/class?
thanks for clearing this up.
You can retrieve only some columns by returning the result as an anonymous type. That can look like this:
var selectedUserId = 19;
var userInfo = from u in context.Users
where u.UserId == selectedUserId
select new { u.UserId, u.Password };
It can also look like this, if you don't use query syntax:
var selectedUserId = 19;
var userInfo = context.Users
.Where(u => u.UserId == selectedUserId)
.Select(u => new { u.UserId, u.Password });
That will return an IQueryable instance, you can add a call to .SingleOrDefault() or .FirstOrDefault() at the end to get back a single object of your anonymous type.
Yes it can be done with Linq to SQL, as bdukes and pcampbell has shown, but it can be done with NHibernate as well.
For example you could execute the following query, which would return a list of object[]:
var userData = session.CreateQuery(
"select u.Username, u.Password from User u").List();
If you want to you could select only some of the fields, but still get back an entity, so that you don't have to mess with object arrays. This can be done with HQL and Critera as well.
For example, if the user class has a constructor that takes a username and password:
var users = session.CreateQuery(
"select new User(u.Username, u.Password) from User u").List<User>();
NHibernate can do this too:
var selectedUserId = 19;
var userInfo = from u in session.Linq<Users>()
where u.UserId == selectedUserId
select new { Id = u.UserId, Password = u.Password };
What it can't do is return a User entity with lazily loaded columns. I have no idea if Linq to SQL can lazy load individual columns.
What this is doing is a projection.
Absolutely it can do what you like. Try and return those fields into an anonymous type. For more info, check out this article by Scott Guthrie on Anonymous Types. Here's an example:
var twoFields = from c in db.Customers
where c.ID == someCustomerID
select new { ID = c.ID, Name = c.Name}
//use your new object called twoFields
Console.WriteLine(twoFields.Name);