Do not set to “closed (won't fix)” from the very first moment! :)

Problem/Motivation

Profile2 is an “user centric” entity. Profile2 types are attached to users. Several profile types can be bundled around a user, defining an individual or a person. Generally this is good. With Profile2, simple users (only with username, e-mail and password) becomes “real persons” having name, gender, postal address, position, title, etc.

But how we deal with cases where we need to manage people, regardless if they are users or not? It's easy when all are either users or non-users. But when some of them are registered users and other (in the same time) are just individuals? In such cases we cannot, for example, build a View of all "individuals" by adding in the same list individuals that are users and individuals that aren't. It would be nice to have profile types that can be attached to users (how Profile2 works now) but in the same time letting them be attached to other/any entity types.

Use case: http://drupal.org/node/1296746.

This is a pain right now because Profile2 attachment to User entity is hardcoded inside the module.

Proposed resolution

The proposed solution is to decouple the Profile2 <=> User relation from the main module by abstracting the relation between the two entity types and build a new Profile2 User (profile2_user) module that implements the Profile2 <=> User relation. Profile2 Page will depend on the new Profile2 User module.

Other modules may define custom relations between Profile2 and arbitrary entities, like Node, by implementing some hooks.

A sandbox project with the new Profile2 can be found here: http://drupal.org/sandbox/claudiucristea/1336766

Remaining tasks

Only the Profile2MetadataController was not reworked. I need some help here.

User interface changes

On admin/structure/profiles/manage/[PROFILE_TYPE] page, admins must select also entities to whom that Profile2 profile type will be attached. For now, for profiles attached to users may be displayed both: as page (if profile2_page is enabled) and in user account too.

API changes

Database changes

  • Before, the user entity was referenced in {profile} table by the uid field. The patch is replacing {profile}.uid with {profile}.entity_type and {profile}.entity_id.
  • A new table {profile_type_entity} was created. This table stores how profile types are attached to which entity.

New sub-module Profile2 User

A new module Profile2 User (profile2_user) was built. This module has Profile2 (profile2) as dependency and must be enabled in order to have the existing “user profiles” functionality available. “User specific” code was moved in this new module. The update.php will migrate all existing profile types and profiles, make them "user profiles".

New hooks

  • hook_profile2_info(): This hook allow modules to attach Profile2 profiles to arbitrary entities (not only to User entity). The new profile2_user module implements this hook.
  • hook_profile2_uri_callback_alter(): Allow modules to alter the previous settings. Eg. “Profile2 Page” module will alter “Profile2 User” entity callback.
  • hook_profile2_submitted_profile_alter(&$profile, &$form_state): Alters a profile that has been submitted through FAPI just before the profile is passed to profile2_save().

Renamed functions

Due to the fact that some functionality was moved from main “Profile2” module to the new “Profile2 User” module some functions were renamed.

Old name New name
profile2_load_by_user() profile2_user_load_by_user()
profile2_by_uid_load() profile2_user_by_uid_load()

See profile2.api.php for a complete list.

Sub-modules locations changed

Modules from sites/all/modules/profile2/contrib were moved in sites/all/modules/profile2/contrib/profile2_user and sites/all/modules/profile2/contrib/profile2_og_access.

Original report by claudiu.cristea

N/A

Comments

claudiu.cristea’s picture

Status: Active » Needs review
StatusFileSize
new82.87 KB

Here's the patch.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Status: Needs review » Needs work

Forgot to run tests locally.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new82.91 KB

Fixed some access bug.

claudiu.cristea’s picture

StatusFileSize
new82.85 KB

Some typo fixes.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

joachim’s picture

> hook_profile2_info(): This hook allow modules to attach Profile2 profiles to arbitrary entities (not only to User entity).

I don't really think that attaching profile2s to any entity is the way to go.

> But how we deal with cases where we need to manage people, regardless if they are users or not? It's easy when all are either users or non-users. But when some of them are registered users and other (in the same time) are just individuals?

Given that Profile2 module's aim is to get into Core for D8, I'm not sure this is going to fly -- I tried to make a case for it back at DrupalCon Copenhagen IIRC.

However, 'people may or may not be users' is the goal of my sandbox project here: http://drupal.org/sandbox/joachim/1260650 -- perhaps you'd like to take a look?

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

StatusFileSize
new82.35 KB

@joachim,

I don't really think that attaching profile2s to any entity is the way to go.

Why? You just did it with User entity but it's hard-coded. What I'm proposing here is to abstract that attachment in a thin, transparent, API manner. Applying this patch and running update.php makes no real changes to how Profile2 works. It only brings in the movie a new module (Profile2 User). Of course, some small API changes may impact on other modules but is not the first API change in the Drupal History.

Looking in the code, everything is more clear and logic now. Similar with "Profile2 User", developers may create other small modules that allows profiles to be attached to other entities than user. This feature just opens the door. No overhead, no complex code... just splitting the logic inside the module.

Given that Profile2 module's aim is to get into Core for D8

I'm not very happy pushing Profile2 to core. This means that we will have new Profile2 stable releases only when Drupal itself will have stable releases. The last development cycle (for D7), took 3 years! That means no Profile2 stable releases with new features in 3 years. Also there are discussions around keeping the core as small as it needs. See #1255674: [meta] Make core maintainable. Also there's a bigger community involvement around contrib modules. Core, generally, is keeping many developers far for contributing.

However, 'people may or may not be users' is the goal of my sandbox project here: http://drupal.org/sandbox/joachim/1260650 -- perhaps you'd like to take a look?

Nice work! Related to "people may or may not be users", it seems to me a too complicated and heavy approach. Keeping profiles uid to NULL in the "profile" table while maintaining the relation in other table. Hmmm... The straight way would be splitting, in "profile" table, from uid (which means "User") to a pair of entity_type and entity_id (which means "any entity") and adding the uniqueness constrain. Simple, not?

Please keep this ticket open to see more comments from other users.

I'm attaching a new patch that makes the implementation of new relations more clean and "API driven".

joachim’s picture

> The straight way would be splitting, in "profile" table, from uid (which means "User") to a pair of entity_type and entity_id (which means "any entity") and adding the uniqueness constrain. Simple, not?

Yes and no... what happens when your non-user (which I assume you represent with another type of entity) creates an account?
This is why we went with the concept of a 'party' entity which remains in existence when a user is created.

A lot of detail about this was figured out back at DrupalCon Copenhagen, and you'll find more detailed explanations and rationales here: groups.drupal.org/crm-api

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Issue summary: View changes

Updated issue summary.

claudiu.cristea’s picture

Yes and no... what happens when your non-user (which I assume you represent with another type of entity) creates an account?

That shouldn't be Profile2 responsibility. That should be handled by the module that implements the non-user entity. In some cases you may want to persist that entity (like party), in other cases you may want to drop it in favor of user. I'm not dealing in this patch with that, I'm only making Profile2 open to other entities.

This is why we went with the concept of a 'party' entity which remains in existence when a user is created

This is not in collision with my proposal. As I saw, 'party' is linking to Profile2 using profile.pid. It's up to you to keep profiles linked to 'party' or 'user' or both. Party is good but it cannot be an overall solution (it is to specialized). My proposal is not a final solution but opens the door for implementing final solutions.

I partially attended that BoF in Copenhagen... Well, a CRM is a high-level solution and seems that crm_party is addressing that. My proposal for Profile2 module is something at API level. Party can use it too and adapt it in a CRM-way.

Thanks.

Status: Needs review » Needs work

The last submitted patch, extend-profile2-1340878-6.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new81.88 KB

The patch was not in correct format.

Status: Needs review » Needs work

The last submitted patch, extend-profile2-1340878-9.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review

#6: extend-profile2-1340878-6.patch queued for re-testing.

claudiu.cristea’s picture

StatusFileSize
new82.36 KB

Fixed the patch

garphy’s picture

@claudiu.cristea: I think your proposal is the way to go. The ability to create Profile entities that are not forcibly bound to an user is a key feature. The @joachim proposal of Party module creates an additional layer of complexity on the data model whereas yours simplify it while retaining the ability to implement both a user-related model, a -related model or a model of Profile entities totally unrelated to anything.

If Profiles aims to be more a "Profiles Pages" module I think it'll forget numerous use cases...

And for the D8 inclusion goal... man... that's really really not a good idea. This will be an additional bloat of code we will all be forced to carry when it's really not needed in a vast majority of Drupal use case : we're not all building "community sites" with drupal.

fago’s picture

Title: Extend Profile2 » Make profile2 not user-centric
Status: Needs review » Needs work

@core:
Whether profile2 makes it into core or not, the goal of it is to build the replacement for the old core profile module using entities. That is, it should be simple and match the use-case of the 90%, while extensible.

Nice work! Related to "people may or may not be users", it seems to me a too complicated and heavy approach. Keeping profiles uid to NULL in the "profile" table while maintaining the relation in other table. Hmmm...

Honestly, to me the possibility to attach a profile to a user account is what makes a profile a profile. Else, I'd see no difference to any other generic entity-types or nodes. Also I don't see a problem with having a NULL value if there is no user.

Next, I don't really get what's the use-case of attaching profiles to other entities nor why we need to re-work the module to do so, as you can easily attached it to other entities by many means (entity reference, relation, your own-table, ...).

Lastly, profile2 is stable so re-architecturing it would require another branch and force developers to update their code. That's not on the table unless there are very good reasons to.

Still, I'd be happy to discuss any improvements necessary to fit your use-case too.

@claudiu.cristea: I think your proposal is the way to go. The ability to create Profile entities that are not forcibly bound to an user is a key feature.

The ability to create profile entities not related to a user is already there. The API allows it, while the UI does not account for it. Other modules such as party can take on the UI.

claudiu.cristea’s picture

@fago: Honestly, to me the possibility to attach a profile to a user account is what makes a profile a profile. Else, I'd see no difference to any other generic entity-types or nodes [...]

Next, I don't really get what's the use-case of attaching profiles to other entities nor why we need to re-work the module to do so, as you can easily attached it to other entities by many means (entity reference, relation, your own-table, ...).

You are right in the way that simply linking a Profile2 types to an arbitrary entity makes no difference to nodes. But, here comes the problem: I need to aggregate (in Views) people, not only users. People are collections of Profile2 Types bundled around "something". Some of them may be users too, some of them not. What they have in common is that they have all Profile2 but not necessary are linked to users.

Here's a use case http://drupal.org/node/1296746 but you can easily imagine that every CRM system may face the same situation: People that are (also) users together with people that aren't. How to build with Views a list of all people that are male and younger than 18 years when some of them are users and some not?

@fago: Lastly, profile2 is stable so re-architecturing it would require another branch and force developers to update their code. That's not on the table unless there are very good reasons to.

I can see the pain. Fortunately when the module upgrade is deployed all the existing Profiles2 are kept and the new profile2_user module is enabled on DB update. There is a stable and smooth upgrade path via hook_update_N(). The only problem remain the API changes. Right now I count only 2 API changes profile2_load_by_user() => profile2_user_load_by_user() and profile2_by_uid_load() => profile2_user_by_uid_load() but I may be wrong. Anyway, is not the first API change in the Drupal World... will be documented, will open tickets in major modules that rely on those API functions, etc. Keeping some wrapper functions for backward compatibility should work?

The ability to create profile entities not related to a user is already there. The API allows it, while the UI does not account for it. Other modules such as party can take on the UI.

I know, I know... I saw the Party module. Looks good but internally is too complicated. I'm sure that is "doing the job" for the case it was build but, for example, I wouldn't use it on my sites because is too particularized. This solution isn't solve any general case too. Is only an Architecture/API change not providing any UI new feature in that direction. But what it brings is a new layer of abstraction that makes modules like Party handle things more simple, more clean and transparent.

Last but not list...

It would be not too complicated to imagine a brand new module bringing that feature (people being users or/and not). But why creating a Profile3 module? Why duplicating a lot of work that Profile2 is doing well? Except the small API change everything will work as now. There's no overhead this change will bring. True, I didn't benchmark - but looking in the code, the only thing that may impact is replacing {profile}.uid with the pair {profile}.entity_type and {profile}.entity_id.

joachim’s picture

> I saw the Party module. Looks good but internally is too complicated. I'm sure that is "doing the job" for the case it was build but, for example, I wouldn't use it on my sites because is too particularized.

Could you file an issue on Party explaining more about this?
The goal of Party was to cater for exactly the use case here: provide a general entity that covers both users and non-users.

I agree it's got rather complex, but perhaps with input from you we can tidy it up a bit and see about moving some of the more complex functionality to submodules.

claudiu.cristea’s picture

Status: Needs work » Needs review

Switching back to "Needs review"

fago’s picture

Status: Needs review » Postponed (maintainer needs more info)

Still, you have not provided a reason we need to make this architecture shift to achieve that.

Here's a use case http://drupal.org/node/1296746 but you can easily imagine that every CRM system may face the same situation: People that are (also) users together with people that aren't. How to build with Views a list of all people that are male and younger than 18 years when some of them are users and some not?

But that was profile2 profiles already do? They optionally refer to user accounts, so you can safely represent each person with a profile.

Then, still you can go and relate profiles with any entity without any architecture shift. Leave the uid NULL, create your own table for relating profiles to any entity type and you are done or use an existing solution for it (entity reference, relation). Where is the problem?

fago’s picture

Issue summary: View changes

Updated issue summary.