CakePHP need to know model ID before saving - cakephp

My situation:
I have a form based on several models linked together.
In background I have a routine for saving images , based on ajax call to a particular controller which uploads the image and displays it on a textarea in realtime.
My problem applies when I have to save the record first time, because the ajax routine needs to know the ID of the record to associate the image , but this will be created only after save() will be called.
Is there a way to obtain a ID before the model saves? or some other workaround
Thanks in advance to everyone
Rudy

I think #RichardAtHome provides a good input on this. I'll just add a third solution you may want to consider:
Use universally unique identifiers (UUID) for that model's primary key. CakePHP offers a way to generate them via the String utility. So what you would do is generate the id when the page loads with String::uuid() and then send it as part of the ajax requests.

A couple of solutions:
Change your workflow, so that the images can only be linked to the main model once the main model has been saved (and has an id).
Don't save your images to the database until the main model has been created. You could store them in a session, or just keep appending fields to the end of the form as the user adds more fields.

Option1:
You could try using UUIDs instead of INTs. That way, you can just use CakePHP to generate an ID: String::uuid();.
After you create it, you can populate an id field that would then make the item save with that id.
I'm not sure I'd rely on it's uniqueness if I were doing banking software or something critical, but - it's something like 1 in a billion chance to be duplicate, and for normal websites/software, it seems like that would suffice.
Option2:
In a lot of our projects, we've found it helpful to have a very simple "create a Thing" page with just a title field, then, once saved, it takes them immediately to the more in-depth "edit" page where they can upload files, save extra data...etc etc. Almost like a pg1, pg2. That way, files, as well as any other dynamic ajax-driven data will have an ID to work with.

Probably not, because the ID will be set by the Database, and not CakePhp, and the id can only be known after the database has auto-incremented the ID field...
Not sure how your flow is, but I guess you can save a dummy/empty row to the database without all the associations when the controller gets the page (when $this->data is empty). You know have an ID to an empty row in the database that is like "12, NULL, NULL, NULL, NULL, NULL, ...". You can then store the ID of that dummy record (or do $this->set("id", $id) to pass it to the view to use in your ajax-calls).
After the POST of your page you can then use the ID to store all other information in dummy row, or delete the dummy row from the database when something fails/user navigates away...

Related

Best way to store comments with mentions (#FirstName) in database

Was wondering what is the best way to store comments in a database (sql) that allows mentioning of other users by a non-unique natural name?
E.g. This is a comment for #John.
The client application would also need to detect and link to corresponding user profile if his/her name was clicked.
My initial thought was to replace the user's first name with the id and some metadata and store that in the DB: This is a comment for <John_51/> where 51 is the id of that user. Clients can then parse that and display the appropriate user name and profile links.
Is this a good approach?
Some background:
What I would like to achieve is similar to facebook posts where it allows you to 'tag' a user by just mentioning their name (not the unique username) in a post. It doesn't have to be as complex as facebook as what I need it for isn't for a post, but just comments (which can only be text, as opposed to posts which could be text mixed with videos/images/etc).
The solution would affect the database side (how the comments are stored) and also the client side (how the comments are parsed and displayed to the user). The clients are mobile apps for iOS and Android but also looking to expand to a web application as well.
I don't think the language matters as much but for completeness sake, I'm using Python's Flask with SQLAlchemy frameworks on the backend.
Current DB schema for comments
COMMENT TABLE:
id (<PK>)
post_id (id of the post that the comment is for: <FK on a post object>)
author_id (id of the creator of the comment: <FK on a user object>)
text (comment text: <String>)
timestamp (comment date: <Date>)
Edit:
I ended up going with metadata in the comment. E.g.
Hey <mention userid="785" tagname="JohnnyBravo"/>!
I included the user's name (tagname) as well so that client application can extract the name directly from the comment text instead of adding another step to look up who user 785 is.
The big problem here is if the username is not a stable reference, you need to abstract it to an id reference, while still keeping the the text reconstructable, but the references queryable.
Embedded collections and dynamic typing are a great option if you're using a NoSQL database. It would be fairly straightforward.
{
_id: ...,
text: [
"Wow ",
51,
", your selfie looks really great, even better than ",
72,
"'s does."
],
...
}
That way you could query references, while still easily reconstructing the content. BUT since you're using SQLAlchemy, that's a no go. Your methodology seems fine, but because your doing magic in the string you'll need to escape your delimiters, (as well as escape the escape character) if they exist in the text. Personally, I would use # as the delimiter since it's already a special character. You'd also need to identify the end of the id, in case the user sticks a bunch of numbers after the #mention, so
Wow #51#, your selfie looks really great, even better than #72#'s does. email me! john\#foo.com. Division time!!! with backslashes! 12\\4 = 3
IF querying posts for references is also important to you. You'll also need to maintain a separate POST__USER junction table that stores a row for the post and for each user id, so that when you load an object into memory, you can construct a collection. You could decide to add the junction table later, but it would be a fairly expensive migration.
If #name is not unique,you have to somehow associate the non-unique name, via the session, with the unique owner of the natural name, and do this ideally before storing it in the database. Storing a non-unique name in the database, if it cannot be resolved to its unique owner, is not of much value.
Since you mention "sql" I assume you're using a relational database. If that is the case, once you have resolved #name to its unique owner, I would create a one-to-many relationship between posting or comment and userids; that would allow a comment or post to reference more than one user.
TABLE: COMMENT_MENTIONEDUSERS
commentid
userid
I would recommend storing the comment as markdown since it's now quite widespread. In your case, "This is a comment for [#John](/user/johnID)".
Markdown is pretty standard and you shouldn't have an issue finding a package for editing / viewing.

Yii Check if entry exists in database before creating new one

I'm pretty new to yii.
I have a form that creates and saves multiple AR models to the database. The problem is that in many cases new entries are actually identical to existing ones. I would like to reduce/ eliminate this kind data redundancy.
The form has 3 entities:
- the main model
- client model
- product model
Many times, product and client will already exist in the database.
Product and Client are referenced through foreign keys in the main model.
I want to know how would it be possible to do the following:
as I type a client's name or phone number, yii searches in the client table and display results as suggestions, through ajax.
if I select one of the suggestions, the Client AR should be populated with that database entry.
when the form is submitted:
if an existing client was selected, use that client's id inside the main model. Do not create a duplicate client in the database.
if client wasn't found in the existing records, create a new one with the provided form data.
I apologize for the bad formatting, this is my second time posting a question. If I wasn't very clear in what I am looking for, please ask for clarification. This is something I would really like to learn.

Saving a user in CakePHP with ACL group name specified rather than group id

I have set up a Cake 2.3.1 app with ACL following this tutorial so I have a users table and a groups table.
Everything is working fine, but I'm a little confused about how I should go about saving new users since I don't want the user to be able to select their own group. The group will be chosen based on a number of things that the user inputs on the form so it will be determined by conditional statements in the controller.
At the moment I'm doing this:
$this->request->data['User']['group_id'] = '2'; (or 1, 3 or 4 depending on conditional statements in the controller).
Then, I'm doing a save on $this->data. This works but I'd like to be able to specify the group name rather than hardcode the id so that when we move to production and clear the database, it won't matter if the groups are added in a different order.
I see two ways of doing this.
The first I haven't tried, but it would be to change the primary key of the group table to the name of the group and set the primaryKey variable in the Group model and all subsequent reference and associations with this table in all the other models. That would probably bring you more than a headache and I wouldn't recommend it because you'd probably break the ACL component one way or another.
The other way is what I'd do, and it requires a bit of programming on your part (Cake doesn't have an easy way of doing this that I know of).
In the User model, put a beforeSave and in that function, create a dummy variable with the group name, do the lookup of the name and insert the correct id, and delete that variable.
On User.ctp model
public function beforeSave() {
if (isset($this->data[$this->alias]['group_name'])) {
//do the lookup with the group_name
$group = $this->Group->find('first', array('conditions'=>array('name'=>$this->data[$this->alias]['group_name'])));
if (count($group) <= 0)
//... throw an Exception or handle any way you want this case
$real_group_id = $group['Group']['id'];
$this->data[$this->alias]['group_id'] = $real_group_id;
unset($this->data[$this->alias]['group_name']);
}
return true; //remember this is important! otherwise the save will stop
}
When you want to change the user's group or add another user, just do this in the controller
$data_to_save['User']['group_name'] = 'webuser';
and the beforeSave function will take care of the rest.
I realize you asked for a Cake-easy-way of doing this, but I guess there's none (not sure though, feel free to correct me), the beforeSave function could save you a lot of repetitive code on the controllers. The only thing you'd have to be careful when clearing the database or changing group ids is to reassociate them correctly with the users already in the database, but since is a "moving to production" change, those users already predefined should be few or none.

cakePHP HABTM, am I getting it all wrong?

I understood that every new row, causes the deletion of the rows that were there before?
What is the idea behind it? I don't believe that it is ..
So, what am i getting wrong?
Edit A
I have a form that adds a store to the Stores table. the store have a column named owner_id which is associated to the Users table through a belongsTo relationship.
There is also a table named stores_users that supposed to store the mangers for each store, using the HABTM relationship.
For this table there is a form with an email field, that connects the user to the store by saving the record directly to the stores_users table.
So, there is no full HABTM save anywhere, if I understand the term correctly.
So, my questions are:
Should I expect problems using it this way?
Can you advice me about how to it, if my method is not the proper way?
How can I use the stored data, using $this->User->find(...) to get all the stores that the user can manage?
yes, thats the default behavior of HABTM in cakephp
although this is not on "every row", but "every HABTM save".
this is working IF you always provide all HABTM values.
and with baked views according to the specifications for such HABTM this is all working out of the box.
if you change the default behavior (old ones get not deleted) you will need to make sure that there are no duplicates. there are behaviors out there, I think, which try to accomplish that.
but I would recommend for you to build your forms the way that the default behavior of cake can do its job.
Example:
IS: 1,3,6 (in DB for this key)
NEW: 2,3,6 (coming from form)
(cake deletes 1,3,6 and adds 2,3,6)
=> overall result (forgetting about different primary keys): "1" deleted, "2" added
so it might not be the most resource sparing way but its sure the easiest and fastest.

Database design: Splitting a blog entry into multiple pages

What is the best database strategy for paginating a blog entry or page content where some entries may be a single page and some may span multiple pages? Note: The content would be article-like rather than a list of items.
The method that I'm currently considering is storing all of the content in a single text field and using a page separator like {pagebreak}. Upon retrieval, the content would be split into an array by the page separator and then the page would display the appropriate index. Is this the best way to go about it, or is there a better approach?
I think your current idea would be the best option. Makes it a lot easier to move the page breaks if you ever want to, or to put them in when you originally compose the article. Also allows you to have a print page option, where the entire article is in 1 field.
the easy way (now, but you'll pay later )is to store the entire article within one text field, but you give up some display control because you'll might need to put some html in that text. If you put html in the text, you'll have a lot of data to fix if you ever change your web page's look/feel. This may not be an issue
As a rule I try not to ever put html into the database. You might be better off using XML to define your article, and store that in one text field, so your application can properly render the contents in a dynamic way. You could store page breaks in the XML, or let the app read in the entire article and split it up dynamically based on your current look/feel.
You can use my "poor man's CMS" schema (below) if you don't want to use XML. It will give you more control over the formatting than the "all text in one field" method.
these are just a wild guess based on your question
tables:
Articles
--------
ArticleID int --primary key
ArticleStatus char(1) --"A"ctive, "P"ending review, "D"eleted, etc..
ArticleAuthor varchar(100) --or int FK to a "people" table
DateWritten datetime
DateToDisplay datetime
etc...
ArticleContent
--------------
ArticleID int --primary key
Location int --primary key, will be the order to display the article content, 1,2,3,4
ContentType char(1) --"T"ext, "I"mage, "L"ink, "P"age break
ArticleContentText
------------------
ArticleID int --primary key
Location int --primary key
FormatStyle char(1) --"X"extra large, "N"ormal, "C"ode fragment
ArticleText text
ArticleContentImage
-------------------
ArticleID int --primary key
Location int --primary key
AtricleImagePath varchar(200)
AtricleImageName varchar(200)
You can still put the entire article in one field, but you can split it up if it contains different types of "things".
If you have an article about PHP code with examples, the "one field method" would force you to put html in the text to format the code examples. with this model, you store what is what, and let the application display it properly. You can add and expand different types, put page breaks in, remove them. You can store your content in multiple "ArticleContentText" rows each representing a page break, or include "ArticleContent" rows that specify page breakes. You could let the app read the entire article and then only display what it wants.
I think the correct approach is what you've mentioned: the entry should be stored in the database as a single entry, and then you can use markup / the UI layer to determine where pagebreaks or other formatting should occur.
The database design shouldn't be influenced by the UI concepts - because you might decide to change how they are displayed down the road, and you need your database to be consistent.
You're much better off leaving formatting like this on the client side. Let the database hold your data and your application present it to the user in the correct format.
It seems to me like a good solution. This way you will have your article as one piece and have the possibility to paginate it when necesary.

Resources