The system itself is pretty easy to understand but tricky to implement. Moreover, security reasons made me think how to do that.
I was thinking to make the function work in frontend firebase scripts simply doing everything there as checking if there is already like/dislike posted by this user and remove/add/switch if user clicked. The problem lies in security of this method: can't user create a new function which won't check if the like was posted?
And if that's possible, how should this system work? Right now my logic:
Clicked like:
locally activate/deactivate like button and remove dislike active class if on
check docs for this user/doc like
`1`? -> remove this doc from collection
`0`? -> switch to `1`, because `0` is dislike
`undefined`? -> create doc with `vote: 1`
change (+1/-1 or +2/-2) the value of post votes fields
Same for dislike. But this sounds really complicated as for such a small feature. Maybe it is possible to wave additional collection with user/votes without losing that level of security? Or using http-triggers may help with this somehow? This feature would be much easier on some PHP-like languages, so I am freaking out right now.
Here are my assumptions.
You have a post with a unique id, let's call it post_id
You have a user with a unique id, let's call it user_id
There are 3 valid states: (Undefined), (Like), (Dislike)
Basic process follows
To store the likes/dislikes, you create a collection called feelings which uses post_id+':'+user_id as it's document id (this makes it easy to look up).
Documents in feelings have a single field called state that stores -1 for dislike, 1 for like.
As you mention, you can simply set or overwrite this value to whatever the user desires. If they decide to remove their 'feeling' and neither like or dislike, issue a delete command (this is cheaper than doing a write to set state to 0).
Use Cloud Functions to listen to the feelings collection and update the post documents like/dislike count based on how this state changes (or gets created/deleted).
Security Rules can enforce only allow states of -1 and 1, and if you're using Firebase Auth, you can trivially enforce only allowing the user matching user_id from being able to change state.
What have you got now?
You now have a system with the following properties:
Users can like and dislike posts
Users can remove and/or change their like/dislikes
Users can only like or dislike a post once - they cannot do it multiple times
Only valid states (like, dislike) can be written to the database
Only the user can update their likes/dislikes
Scalable: Whether it's 10 posts, or millions of posts, this system will work
Bonus credit
Using the same Cloud Functions event you register to update counts, you can also use this to add or remove from a list of user ids in both a like and dislike array. This would allow you to list out users who liked or disliked a post without have to query for each individual document in the feelings collection
Also remember that Cloud Functions has is a small chance it gets triggered more than once for a single event. If you want the counts to be guaranteed to be accurate, either make the code idempotent, or just have a manually triggered 'recount' process you can trigger if you or a user detects the counts seem off by one.
If somebody's wondering for Realtime Database!
var ref = firebase.database().ref(selectchapter + questionId + user_id);
ref.once("value", function (snapshot) {
var exists = snapshot.val() !== null;
if (!exists) {
firebase
.database()
.ref(
selectchapter +
questionId + user_id
)
.set({
status: 1
}).then(function () {
firebase.database().ref(q +
questionId + "likes").transaction(function (likes) {
return (likes || 0) + 1
});
});
}
});
Related
I'm managing a company website, where we have to display our products. We however do not want to handle the admin edit for this CPT, nor offer the ability to access to the form. But we have to read some product data form the admin edit page. All has to be created or updated via our CRM platform automatically.
For this matter, I already setup a CPT (wprc_pr) and registered 6 custom hierarchical terms: 1 generic for the types (wprc_pr_type) and 5 targeting each types available: wprc_pr_rb, wprc_pr_sp, wprc_pr_pe, wprc_pr_ce and wprc_pr_pr. All those taxonomies are required for filtering purposes (was the old way of working, maybe not the best, opened to suggestions here). We happen to come out with archive pages links looking like site.tld/generic/specific-parent/specific-child/ which is what is desired here.
I have a internal tool, nodeJS based, to batch create products from our CRM. The job is simple: get all products not yet pushed to the website, format a new post, push it to the WP REST API, wait for response, updated CRM data in consequence, and proceed to next product. Handle about 1600 products today on trialn each gone fine
The issue for now is that in order for me to put the correct terms to the new post, I have to compute for each product the generic type and specific type children.
I handled that by creating 6 files, one for each taxonomy. Each file is basically a giant JS object with the id from the CRM as a key, and the term id as a value. My script handles the category assertion like that:
wp_taxonomy = [jsTaxonomyMapper[crm_id1][crm_id2]] // or [] if not found
I have to say it is working pretty well, and that I could stop here. But I will have to take that computing to the wp_after_insert_post hook, in order to reaffect the post to the desired category on updated if something changed on the CRM.
Not quite difficult, but if I happen to add category on the CRM, I'll have to manually edit my mappers to add the new terms, and believe me that's a hassle.
Not waiting for a full solution here, but a way to work the thing. Maybe a way to computed those mappers and store their values in the options table maybe, or have a mapper class, I don't know at all.
Additional information:
Data from the CRM comes as integers (ids corresponding to a label) and the mappers today consist of 6 arrays (nested or not), about 600 total entries.
If you have something for me, or even suggestions to simplify the process, I'll go with it.
Thanks.
EDIT :
Went with another approach, see comment below.
I'm using FeathersJS and ReactJs to build an application and now I am stuck in a certain point.
The user interface I'm working on is a table and I have a navigation bar to control the data exhibited, like in the image.
In the select users may choose how many results they are going to see in a page. The navigation buttons move among different pages. Every time one of these thing change I calculate
let skip = (this.state.page - 1) * this.state.limit;
let url = '/users?$sort[name]=1&$skip=' + skip + '&$limit=' + this.state.limit;
and then make a REST call to Feathers backend. This is working perfectly well.
The red arrow indicates my problem.
When the user types part of a name in this input field I need to search the Users table to find all users with the typed string in the name, whatever the position and preferably case insensitive. In this case I am creating an url like
let url = '/users?name=<typed input'>;
and then making the REST call.
It happens that this call will try to find a name equal to , and this is not what I need. Then I am assuming I'll have to customize my find() method to perform differently when it receives the two different sets of parameters, like in
users.class.js
const { Service } = require('feathers-sequelize');
exports.Users = class Users extends Service {
find(data,params) {
if (data.query.name) {
// Do something to get the search I need
}
return super.find(data,params);
}
};
But I really don't know to proceed from this point on.
Should I implement a Sequelize raw query inside this if? What would be the best way to get the data I need?
This isn't really a Feathers question, or even a Sequelize question, but a database function. It depends on what your database is! For MySQL, you can use the LIKE command to search for a substring. Google how to make it case insensitive, if need. For the Sequelize query I believe something like this would work:
query = {
name: { $like: '%searchterm%'}
}
Is there an easy way to save a recordset, i mean multiple records, but only the "new ones"?
I have a table users and a users form in the view. First field you must enter is passport number, so if user already exists in database the rest of the form will be auto completed and disabled to prevent changes but if passport dont exist then you have to enter all data. As anyone can change those existing users data controls from the browser even if they are disabled, i want to make sure only new records are saved in database. First of all i thought i could find again in database and delete existing users from recordset before save, but i wonder if there is a more elegant approach.
Ty in advance.
I write this here, cause comments are too short:
Thank you for your answer, André. I'm sorry if i didnt explain perfectly, but the form is done by disabling all controls except passport and if passport dont exist (i check it on passport focusout) then the rest of controls are set to enabled. I mean, that is already done. The question was only about the saving.
The validation method you talk about, well i'm actually validating all the controls in the form and i must disable the 'unique' rule so a user can link an existing user to the current bill, otherwise it will fail validation on submit and it wont allow the user to proceed (i did this because it happened to me when testing). The actual setting is much more complicated: the form belongs to a model (bills) which is associated with 4 other models and 2 of those relationships are many to many, bills_users and bills_clients, where users are the persons who do the job and clients pay for it, but I was trying to make the question easier. Anyway, what I am looking for is, in fact, some kind of saving setting which I can't find. In documentation I found "When converting belongsToMany data, you can disable the new entity creation, by using the onlyIds option. When enabled, this option restricts belongsToMany marshalling to only use the _ids key and ignore all other data." The first half of the sentence was promising, but the explanation says different, and I actually tried it without success.
First:
Is there an easy way to save a recordset, i mean multiple records, but only the "new ones"?
Yes there is you can validate it in model, something like this:
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->isUnique('passportNumber'));
return $rules;
}
This will prevent to save a duplicate passport number register, but you can different.
I have a table users and a users form in the view. First field you must enter is passport number, so if user already exists in database the rest of the form will be auto completed and disabled to prevent changes but if passport dont exist then you have to enter all data.
There is two different ways to do this:
First you have your form, you develop an ajax function when you fill the first field (passport number) this ajax function do a request to your controller to search for a passport with that number if find something get data and fill others fields and turn them just readable, else just nothing and let user fill the fields by himself
second add a step-by-step where you first do a form to try find by pass number, user fill this only field with a number then submit, on submit this search for a record, if find fill the entire next step fields, else the next step will be the rest of form with the fields to be filled.
This may help you too: https://book.cakephp.org/3.0/en/orm/validation.html
Tell me how you decided to do :)
This is a design question.
I'm trying to build a booking system in cakephp3.
I've never done something like this with cake before.
I thought the best way might be to -- as the post title suggests -- build up an entity over several forms/actions.
Something like choose location -> enter customer details -> enter special requirements -> review full details and pay
So each of those stages becomes an action within my booking controller. The view for each action submits its content to the next action in the chain, and i use patch entity with the request data, and send the result to the new action's view.
I've started to wonder if this is a good way to do it. One significant problem is that the data from each of the previous actions has to be stored in hidden fields so that it can be resubmitted with the new data from the current action.
I want the data from previous actions to be visible in a read only fashion so I've used the entity that i pass to the view to fill an HTML table. That's nice and it works fine but having to also store that same data in hidden fields is not a very nice way to do it.
I hope this is making sense!
Anyway, I thought I'd post on here for some design guidance as i feel like there is probably a better way to do this. I have considered creating temporary records in the database and just passing the id but i was hoping I wouldn't have to.
Any advice here would be very much appreciated.
Cheers.
I would just store the entity in the DB and then proceed with your other views, getting data from the DB. Pseudo:
public function chooseLocation() {
$ent = new Entitiy();
patchEntity($ent,$this->request->data);
if save entity {
redirect to enterCustomerDetails($ent[id]);
}
}
public function enterCustomerDetails($id) {
$ent = $this->Modelname->get($id);
// patch, save, redirect again ...
}
I made a simple Angular + Firebase app, which you can see here.
I want to add user authentication to it using Firebase, and have successfully been able to create an account and login using Firebase's email and password auth.
The problem is, I don't know how to take the next step to separate each user's data — I want each user to only to be able to change their own data, and show nothing until they're logged in.
The only thing I could think of was to change the firebase URL I'm using to separate the FirebaseArrays I'm using, but that seems super hacky and didn't work when I tried it.
How am I supposed to use the unique uids to create separate, individual user data?
So every user will have a unique id within the authorize table , of which you cannot directly access ( in that you cannot add tables - firebase keeps it locked down for security reasons )
This still allows for a separation based on the id. There will essentially be a books array, You could add an array of users on each book, but that could get redundant. I think the best solution would be to create a user_books array. This will list all the users by id that have books, with the bookID as a nested array
user_books
-Jsdee23dnn23d2n3d
|- sdfEDASED82342dsd : true
|- SDEdsdfwer343dsdf : true
-Jsdeesdfffffs433
|- 43334sdfsrfesrdfg : true
|- sdfsf333fsdfsrrrr : true
This connects the books to the users. So each user has an array of books . This is the target to access when you want the user to perform CRUD operations
var usersRef = new Firebase('https://test.firebaseio-demo.com/user_books');
var ref = usersRef.child('userID');
When you create a book you will need to update both the book table and the user_books table with the selected user(s) associated to the book