Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal for entity list system (Trac #2567) #2567

Closed
4 tasks
elgg-gitbot opened this issue Feb 16, 2013 · 37 comments
Closed
4 tasks

Proposal for entity list system (Trac #2567) #2567

elgg-gitbot opened this issue Feb 16, 2013 · 37 comments

Comments

@elgg-gitbot
Copy link

Original ticket http://trac.elgg.org/ticket/2567 on 40796290-01-22 by trac user mrclay, assigned to trac user mrclay.

I've [http://mrclay.org/index.php/2010/10/17/elgg-core-proposal-new-table-entity_list_items/ posted a proposal] to add an "entity list" system (and at least one new table) in the core. This would greatly ease the creation of features like this:[[BR]]
[[BR]]

  • A “featured items” widget for a group or user. On a group page this could be implemented in left/right lists for more display control[[BR]]
  • “Sticky” entities in discussion lists, or any other object lists[[BR]]
  • A “favorites” list for each user

Working API plugin using relationships for storage: https://github.com/mrclay/Elgg-elggx_lists_api but I suggest the relationship schema move to lists.

TODOs

  • Agree on a set of interfaces
  • Agree on a set of expected behaviors (tests)
  • Compile a list of use cases
  • Document usage pattern for these use cases

Interfaces for discussion

Open questions:

  • Naming conventions
interface Entity {
  // properties
  function getGuid();

  // graph traversal
  function lists(): Map<string, EntityList>;
}

interface Relationship {
  // properties
  function getIndex(): int;
  function setIndex($index);
  function getTimeCreated(): DateTimeImmutable;
  function getWeight(): number;
  function setWeight($weight);

  // graph traversal
  function parent(): Entity;
  function child(): Entity;
}

interface EntityList extends Map<Entity, Relationship>, MutableSequence<Relationship> {
  // properties
  function getName(): string;

  // graph traversal
  function parent(): Entity;
  function children(): MutableSeries<Entity>;
}

interface MutableCollection<Y> extends Collection<T> {
  function add(T $item): void;
  function remove(T $item): void;
}

interface Sequence<T> extends Collection<T> {
  function getAt($index): T;
  function indexOf(T $item): int;
  function slice($startingAtThisIndex = 0, $returnThisNumberOfItems = 0): Series<T>;
}

interface MutableSequence<T> extends Sequence<T>, MutableCollection<T> {
  function insertAt($index, T $item): void;
  function removeAt($index): T;
  function splice($startingAtThisIndex = 0, $removeThisNumberOfItems = 0, T ...$andInsertTheseInTheirPlace): Series<T>;
}

interface Map<K, V> extends Collection<V> {
  function get(K $key): V;
}

interface MutableMap<K, V> extends Map<K, V> {
  function set(K $key, V $value): void;
}

There where method is probably the most mysterious (and difficult to implement). My idea here was to allow something like elgg_get_entities, but with a slightly cleaned up API:

// All items in the List with a weight of exactly 1.0
$list->where(['weight' => 1.0]);

// All items in the List with a weight between 1.0 and 2.0
$list->where([
  'weight' => [
    '>' => 1.0,
    '<' => 2.0,
  ]
]);

I find it a little clearer than the QueryModifier concept, which is too vague (modify how, exactly?).

@elgg-gitbot
Copy link
Author

cash wrote on 40800879-04-26

Another use case is photo albums. The only way to order the photos right now is to store the order as a serialized array.

@elgg-gitbot
Copy link
Author

cash wrote on 40979472-05-22

I was cleaning up the sites library and saw that Ben and Marcus originally had the concept of collections being their own table or entity.

Also, this additional table would solve the pages plugin ordering problems.

@elgg-gitbot
Copy link
Author

Milestone changed to Elgg 1.9 by cash on 40979472-05-22

@elgg-gitbot
Copy link
Author

ewinslow wrote on 41719537-01-08

I would love for collection to be a top-level type instead of group. There are plenty of use-cases for these and right now the only option is to subtype object and implement your own collection interface.

Folders, playlists, bookshelves, albums, friend lists, groups, etc... plenty of use-cases for this.

@elgg-gitbot
Copy link
Author

ewinslow wrote on 41719539-05-26

As for pages, we'd have a nice recursive definition. Pages are collections of pages :)

@elgg-gitbot
Copy link
Author

cash wrote on 41820998-07-12

Tagging this with ElggList and ElggCollection so it's easier to find in the future

@elgg-gitbot
Copy link
Author

trac user kevinjardine wrote on 41978044-04-21

I would not like collections to be top level entities. Currently any entity whatsoever can be a container and I would like to maintain that flexibility. A group is not just a container. It has members and access groups, etc.

I do like the idea of an API to make it easier to manage containers.

@elgg-gitbot
Copy link
Author

cash wrote on 41978234-07-14

http://community.elgg.org/pg/forum/topic/830706/entity-list-spec/

@elgg-gitbot
Copy link
Author

trac user mrclay wrote on 42324086-11-18

Latest thread http://community.elgg.org/pg/forum/topic/835942/elggcollection-plugin-implementation/

@elgg-gitbot
Copy link
Author

trac user mrclay wrote on 42919688-07-30

Friday night hackathon! I'm going to need this soon whether or not it makes it into 1.9, so tonight I did a huge sprint on this and made some great progress.

https://github.com/mrclay/Elgg-leaf/compare/19-collections

I've still not actually executed any of this, but PhpStorm promises everything looks right. It also introduces the ideas discussed in the elgg-dev thread (a query modifier interface, named queries, and a proof-of-concept "entities query API").

Still a few big todos: implement method to relocate an item within the collection (for drag/drop), get rid of all the other unneeded items methods like slice(), write units, specify the plugin API and what/if any global functions we want to create to manage these.

@elgg-gitbot
Copy link
Author

trac user mrclay wrote on 42919701-09-03

BTW, this implementation was rewritten to completely use relationships instead of a new table. Ordering is done via relationship ID (and yes re-ordering will be a pain). The description I have so far: "A collection can be thought of as metadata that stores a list of entities in a way that's optimized for SQL JOINs".

Next step: sticky blog post feature.

@mrclay
Copy link
Member

mrclay commented Aug 21, 2013

Current work on this: https://github.com/mrclay/Elgg-elggx_collections_api

This removes access control (simplifying a ton) and basically makes this a lightweight API for creating relationships. Also adds a function to find collections by entity (was very difficult in the previous model), and some built-in views/actions for creating links to add/remove items. The sorting JS needs refactoring but it was pulled from a working project and you can see how it would work.

/cc @ewinslow @cash @kevinjardine

The fact that the API is inherently directed (forces you to choose the parent entity first) I think makes this easier than the current relationship API, which always leaves me confused about which is guid_one and guid_two.

@mrclay
Copy link
Member

mrclay commented Aug 21, 2013

In production this is being used to allow users to create collections of favorite site objects (including other collections). I use an ElggObject to hold title/description and to provide access control. Also each user's collections are also in a collection, so the user can sort how their collections are displayed. When entities are deleted, the system checks for containing collections and replaces the items with a placeholder "This item was deleted" containing some of the original entity data.

@Srokap @beck24 Your thoughts are welcome on this.

@Srokap
Copy link
Contributor

Srokap commented Aug 21, 2013

I was very loosely following this feature, but in general idea is nice. I see it as encouraging more efficient solution to common use cases. I'm a bit lost about implementation details though. Can we summarize what elements do we have in current solution?

I'm not sure I like marking "This item was deleted", why exactly can't we just notice that entity is gone? Don't you make join with entities table anyway?

What do you mean by "containing some of the original entity data"? Do we duplicate data in DB? Which one? Do you take care of synchronization both places if sth changes?

@mrclay
Copy link
Member

mrclay commented Aug 21, 2013

Sorry for the confusion, my last comment was about how it's getting used on one site. None of that would go in core. The basics for the plugin:

  • Each tuple (entity, name) represents a collection
  • API for altering collection items
  • API for applying collection to elgg_get_entities() in various ways
  • No access control built-in (led to really ugly API and more confusion than worth)
  • Some built-in views and actions for common uses.
  • Uses relationship table (but not Elgg API). Could be easily changed to use its own table.

@ewinslow
Copy link
Contributor

Sounds like we're coming down to something pretty solid here.

Can you give a concrete example of how you envision this working with photo albums or pages? Could we potentially implement friends collections (circles) on top of this too?

@mrclay
Copy link
Member

mrclay commented Feb 9, 2015

I eventually arrived at this API: https://github.com/mrclay/Elgg-elggx_lists_api
There's nothing stopping us from basing this on the current relationship schema, but I propose we at least normalize names. Ideally we'd migrate to this "lists" schema (ignore the bit about ACLs).

@mrclay
Copy link
Member

mrclay commented Feb 9, 2015

Still missing: view to re-order items. The action is done, based on jQuery UI sortable, but I haven't made a dedicated view for it. Challenge being dealing with very long lists.

Also, current API doesn't fire relationship events. I'm not sure what would be ideal here, as re-ordering items involves removing and re-adding relationships... it's kind of its own thing.

@jdalsem
Copy link
Member

jdalsem commented Feb 9, 2015

Weights on relationships? #4462

@mrclay
Copy link
Member

mrclay commented Feb 9, 2015

In my plugin lists are ordered by relationship.id, so in the lists schema I suggest we just call this column "position"

@mrclay
Copy link
Member

mrclay commented Feb 9, 2015

but yes, essentially lists have an order, but if you're using the schema as relationships, you just ignore order

@ewinslow
Copy link
Contributor

API seems nice. I'd even encourage more OO, instead of the *_get_list API you have documented there now. E.g.:

$members = $group->getList('members')
if (!$members->contains($user)) {
  $members->add($user);
}

The main question I would have is how to expose metadata about the relationships (relationship time_created, weight -- if we were to add it -- etc.?)

@propertunist
Copy link

just so you know, i already created a plugin that adds sort functionality to lists and it is running on my site. it would be great to get a core function for that.

@propertunist
Copy link

just a reminder that this is now the ticket that covers the introduction of a generic 'featured items' function, since the original ticket was closed in favor of this one.

@mrclay
Copy link
Member

mrclay commented Feb 11, 2015

If you want a featured items feature, reopen that ticket, but this is for adding the API that (hopefully) will power it.

@propertunist
Copy link

99% of tickets i create get support and are then closed. in this case, the ticket was closed due to it being moved to another ticket. when i viewed that ticket, it too had been closed and moved to this ticket.

@mrclay
Copy link
Member

mrclay commented Feb 11, 2015

I'm sorry? Maybe a community discussion on ticket etiquette would be useful?

@propertunist
Copy link

i think so, yes. there appears to be intent to just close as many as possible, without communicating between all involved

@ewinslow
Copy link
Contributor

@propertunist Feel free to contest the close or request justification. Closing tickets without justification is poor form.

@propertunist
Copy link

you can follow the thread from the original ticket here: #6293
if i contest the issue on the original closed ticket, will anyone read my comment?

@hypeJunction
Copy link
Contributor

Yes, we receive a notification for every comment made here.

@ewinslow
Copy link
Contributor

@mrclay I think a practical next step here would be to write up the interfaces that would be exposed. That gives us a reference from which to work, rather than relying on examples alone which always leaves me thinking "is the whole API covered by this set of examples?"

Here's a start...

// How do we expose "weight" and "time_created?"
interface EntityList extends Iterable, Countable {
  function contains(Entity $e): boolean;
  function add(Entity $e): ???;
  function remove(Entity $e): ???;
}

interface ListsRepository {
  public get($id): List;
  public getFromEntity(Entity $e, $name): List;
}

@mrclay
Copy link
Member

mrclay commented Feb 13, 2015

Lists can't have IDs until we migrate to the new schema.

Rather than starting from scratch again, could we just try to build something on my implementation and find what it's missing, etc. I've put a ton of time into it finding the simplest boundaries I could find to get it work.

@mrclay
Copy link
Member

mrclay commented Feb 13, 2015

Since we're staying on the relationships schema it worth noting that my implementation doesn't fire relationship events. To provide generic actions that could be reused by plugins I do however have a simple embedded permission system (based of elgg_can) to determine who can add/remove/rearrange items. This can be simplified however we see fit.

@ewinslow
Copy link
Contributor

Good observation about not being able to get lists by ID.

We don't have to start the implementation from scratch, but I would like the opportunity to discuss the interface.

@ewinslow
Copy link
Contributor

I put some more time into defining the interfaces necessary for this. It's missing several APIs compared to @mrclay implementation but I believe it doesn't lose any expressiveness and the methods are more "standard", based on my knowledge of collection/list APIs.

@jdalsem
Copy link
Member

jdalsem commented Jul 18, 2018

The usecases mentioned in the original post can easily be achieved by elgg_list_entities. For runtime list manipulation we currently have Elgg\Collections. For now i think this is enough. There is however a need for more persistent lists (that removes the need for complex queries), but that is somethings else.

@jdalsem jdalsem closed this as completed Jul 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

7 participants