Recommended way of relations

In my application (Android) i need to share objects between users in relation of many-to-one - user can have many objects, and back - every object can be related to one or more users. In the application view i need to list all the objects related to the current user.
What is the preferred method of implementation of such bidirectional relationship : ACL or make relationships between the tables, or mix of both?
Hope the question is clear.

Hello, Alex!

Considering that your custom table is called “MyTable”. Declare relationships of type 1:N between tables “MyTable” and “Users”. For “MyTable” this feature is available from “Table schema and permissions” tab ( find this button in top right corner of “Data” tab ), and for “Users” it can be done through “Users -> User properties” tab.
After that you’ll be able to retrieve all related objects for the current user using Data service.
Here is an example ( assuming that relation from “Users” to “MyTable” is called “relatedObjects” ):

Backendless.Data.mapTableToClass( "MyTable", MyTable.class );

BackendlessUser loggedInUser = Backendless.UserService.CurrentUser();

Backendless.Data.of( BackendlessUser.class ).findById( loggedInUser, 1, new AsyncCallback<BackendlessUser>()
{
  @Override
  public void handleResponse( BackendlessUser response )
  {
    MyTable[] relatedObjects =  (MyTable[])response.getProperty( "relatedObjects" );
    // your code comes here...
  }

  @Override
  public void handleFault( BackendlessFault fault )
  {
    Log.e( "APPLICATION_LOG", "Error occured: " + fault.getMessage() );
  }
} );

Hi, Alex,

ACL is about security, so if you need to protect some objects in your table or restrict access to them for some users or roles you use it.
Now lets consider bidirectional relation between Users table and for instance Posts table. You can associate post with user usingownerIdfield. Btw when user is logged in and he or she creates postownerId` would be automatically associated with the user. The relation Post to Users is made via One-to-Many relation.

Best,
Artur.

This makes sense.
Now the question is how can i assign an object (new or existing) to one or more specific users? Suppose i know their names/ids?

I supposed that all posts (as in your example) are created as public use, and then i can control the visibility dynamically by the ACL, so when some user grab the objects from the server, he will query by the ACL property. Something like that.
Is it correct concept?

This is correct. You just need to restrict/allow a certain user/group to be able to “Find” the object.

Btw. if you disallow “Find” to all groups you will only get the user related objects.
So perhaps if you have objects that are only visible to the creator this is a good way to simplify queries.

I just re-read your question.

I think it’s not good practice to give you advice on relationship creation without knowing how you are going to “query your data”.

To me, the main goal is to build relationships best for querying, so you should first tell us how you’re going to query against your data in the future. (on a meta level)

Cheers,
Jens

You can assign a related object by calling methods “setProperty”.
Example for new object:

MyTable myObject = new MyTable();
user.setProperty("relatedObjects", myObject);
Backendless.Data.of( BackendlessUser.class ).save( user, callback );

For existing object:

MyTable myObject = Backendless.Data.of( MyTable.class ).find( "objectId" );
user.setProperty("relatedObjects", myObject);
Backendless.Data.of( BackendlessUser.class ).save( user, callback );

best regards,
Alex Navara

Alex,

Thank you for the help.
Some of your code causes an exception.
I get it on line:

MyTable[] relatedObjects = (MyTable[])response.getProperty( "relatedObjects" );

The exception is:

java.lang.ClassCastException: java.lang.Object[] cannot be cast to com.myRelation.MyTable[]

Why?
Can i cast it to List<> ?

Hi Alex,

Make sure to make the following call before you use any of the APIs:

 Backendless.Data.mapTableToClass( "MyTable", MyTable.class );

This is needed for all related objects so the SDK knows which class a related entities should be materialized as.

Regards,
Mark

I have this line, it still crashes on casting as i said.

I generated the code for the data class from the Backendless console and use it “as is”, just replaced the package name.

I also tried to retrieve the related objects in another way, which i found in one of the examples in the Backendless docs:


BackendlessUser user = Backendless.UserService.login( "userid", "password");
List&lt;String&gt; rels = new ArrayList&lt;String&gt;();
rels.add( "address" );
Backendless.Data.of( BackendlessUser.class ).loadRelations( user, rels );

But on this i also get the exception, which says :

BackendlessException{ code: ‘Internal client exception’, message: ‘null’ }

This call is synchronous and therefore MUST run on a background thread.

Otherwise pass in as parameter a AsyncCallback which makes the call asynchronous.

I see.

I also found some one year old post that discuss this API.
Mark, please consider to update the docs to clarify this point with asynchronous example to save time to inexperienced users.
Some thoughts regarding the casting crash ?

I found the problem.

It seems that it is not enough to make a relations between the tables via the console, but it is “must” to add the new object to the user as a relation after object creation.

final MyTable newItem = new MyTable();
// Fill newItem properties
loggedInUser.setProperty( "relatedObjects", new MyTable[] {newItem } );
Backendless.UserService.update(loggedInUser);

And of cause the user update must be done asynchronously.

Are you saying that even if there is a relation, but there are no actual related objects, then the cast does not work? That makes sense to me, since when the response comes back, the collection of related objects is empty. As a result, the client library does not know what data type to use to create that empty array.