Entity-relationship diagram (ERD) for a blog with project page site

I am trying to learn PHP, SQL and databases as I never used those to build sites from scratch. I made an ERD (see attached) for a site with blog and project page using drawio free app.

Questions:

  1. Is that database structure OK or needs some changes?

  2. For post and users tables I used many-to-many type of arrow to show relationships between user_id columns but I think this is incorrect. What type of relationship it is? Also since there would be only 1 user for this site, is that correct implementation - do I need users table at all?

site-erd.drawio.pdf (73.8 KB)

1 Like

That depends on requirements. Recommendations based on the requirements you provided might not be useful and you will have to say they are not.

That is the type of requirement that you need to provide to oth4ers to get useful results.

Instead of (at least in addition to) providing an ERD, try describing what you need. You do not need to be technical.

Samuel is correct that we cannot evaluate whether it’s fit for purpose without a deeper understanding of the requirements.

However, a few technical details stand out to me.

  1. Your connection lines don’t… really make sense. You’ve used a line indicator for “many to many” everywhere, when that isnt the case. For example: User to Post. You’ve indicated this is a many-to-many relationship by the line. But surely that isnt true - A user may have many posts, but a post must surely only belong to a single user. Likewise, anywhere you’ve got a join table. The purpose of a join table is to turn a Many-to-Many relation into a pair of One-to-Many relations.

  2. project_media contains a field “sort_order” for which a type is not defined.

  3. post_tag contains a FK field “tag_id” which is missing its type (Inherited: int). [EDIT: This actually is probably just you not including the blank space for the combination PK definition, which would have no type)

  4. Be careful about large VARCHAR lengths - older versions of databases dont allow VARCHAR above 255 (though we’re getting to the point where that warning is so old that “older versions” are more like antiquities). The max row size of a table in MySQL is now 65,535 - and that includes all fields of the row. TEXT and/or BLOB types would be preferable, as they don’t consume row size (other than their pointers).

  5. The table seems to indicate a Media entity could exist for which no Project or Post is associated. There may be some cascade deletion considerations (not really an ERD thing, but more for the follow-ups and requirements… this is why requirements are important to include)

  6. Media has both a FK to Project and Post. But Post and Project don’t have a direct relation. Do posts belong to projects? Do they only belong to a project if they use the Media from it?

4 Likes

For a typical blog, the relationship between users and posts is usually one-to-many (one user → many posts), not many-to-many. Each post should have a user_id as a foreign key pointing to the users table. If there’s only one user, you technically don’t need a users table right now. However, it’s still good practice to keep it — it makes your database scalable if you ever add multiple authors later. Structure-wise, keep tables simple: users, posts, projects, and maybe categories or comments if needed.

2 Likes

ERDs are nice, but can benefit from english descriptions for each relationship

for example…

what’s a project? and why aren’t projects related to posts?

how are categories different from tags?

what’s a slug and why is it longer than and different from a name?

2 Likes

Your structure looks mostly solid. The post → users relationship should be one-to-many, not many-to-many — one user can write many posts, but each post has only one author.

Even if you have only one user now, keeping the users table is a good idea — it saves headaches later if you ever add login, roles, or multiple authors.

1 Like

slugs are shell-less mollusks most commonly found in vegetative areas that… :wink:

definitely agree, though if you use good notation on your ERD, those can be incorporated into the lines…
yoinks an image off of lucidchart

2 Likes

speaking of which…

i used to, as a hobby, race snails… had this champion snail, and i thought, hey, if i remove the snail’s shell it might make him even faster

didn’t work, though… if anything, it made him more sluggish

1 Like

public area

Site navigation 4 pages: work | about | blog | contact

work page is a home page and contains the following:

-projects listing (grid of project screenshots), upon clicking on a project screenshot user is taken
to individual (single) project page.

single project page
-project title
-live demo link
-github link
-images (screenshots)
-techstack (HTML, CSS, JS, PHP)
-project description

about page contains the following:

-picture/text

blog page contains the following:

-blog entries, upon clicking on a blog entry user is taken to individual (single) blog post page.
-single blog post page contains the following:
-blog title
-blog text
-images/videos (optional)

contact page

-contact form

admin area

-login/logout

-add/edit/delete projects (add text, images, videos)

-add/edit/delete blog posts (add text, images, videos)

Projects would be for an individual project. Example: site built for a customer or just for fun or software testing project like framework built for practice. Blog post would be about something new you learn and would like to share. Example: new CSS property or something in JavaScript you just figured it out how it works etc. I would prefer to keep projects and posts separate (not related to each other).

My understanding is that categories are meant for broader classification whereas tags to narrow it down.

Example:

Category: CSS

Tag: CSS Grid

My understanding in terms of Many-to-Many relationship was one blog post can have many media items and one media item can belong to many blog posts. Can you describe in more detail which columns should be PK with related FK and in which tables in terms of One-to-Many?

You are right. My connection lines are incorrect.

You’re the one giving the database design, so I cannot say whether this is true or false; I simply take at your word that it is true.

Think about that statement from the database point of view though.
How do you put a many-to-many relationship into a database?

Lets say we’ve got post ID 999, and media ID 876.

post ID 999 occupies a single row in the posts table; media ID 876 occupies a single row in the media table.

A single blog post can have many media references. Well, you cant put that information into the post table, because i’ve got 12 media ID’s for this specific post, and I would have 1 media ID field. You cant put that information into the media table, because i’ve got 15 post IDs for this specific piece of media, and 1 post ID field.

That’s why join tables become necessary. A join table has a set of rows, tied to one ID from each table.

A single post can have many matching rows in the join table; but each row of the join table matches to exactly one post.
That same row matches to exactly one media; the media can match many rows in the join table.

In this way, you have shattered the many-to-many relationship between post and media, into two one-to-many relationships - one between the post table and the join table, and one between the media table and the join table.

The word we’re dancing around here is “cardinality”, for the record. When looking at your lines, the cardinality of the line matters, and depends on your origin point. If you start drawing the line at table1, you ask yourself “One row in this table leads to how many entries in the other?”, and draw at the receiving end of the line, the correct symbol for your answer. (The other half of the symbol, “modality”, is basically the minimum number of relations - generally 0 or 1.)

So lets go back to our example, and look at post and post_media_join.

If i start my line at post, and ask the question “One row in post can have how many entries in post_media_join?”, my answer is “Zero or Many”. So i draw a crows foot with a O on my line leading into post_media_join.

I then need the symbol for the other way around; so I start at post_media_join, and ask myself the same question. “One row in post_media_join can have how many entries in post?”, my answer is “One and only one” (Because if it were 0, the row wouldn’t exist; the row MUST contain a post_id, so its Modality is 1). So I would draw the double-I notation on the line leading into post.

1 Like

AI says that is accurate but it seems misleading to me. I would say that relational databases only support one-to-many (and one-to-one) relations directly and many-to-many relations can be created using join tables.

I think it is more accurate to say “The purpose of a join table is to implement a Many-to-Many relation using a pair of One-to-Many relations.” and AI agrees.

It is the introduction of an additional pseudo-object to allow for the limitations of an inflexible object (the fixed-field size of a row) in representing the relational idea. Sure. Whatever words make you happy with the concept.

In programming terms, a Table is an Array of Rows; a Row is made of primitives; it cannot contain an Array. So we need another object (another Table) with which we can simulate an Array within the Row.

A join table ISNT really an entity unto itself; it’s not a “thing” being represented in the database. It’s a pseudo-entity whose existence is to work within the limitations of a relational database structure (in Normal Form, anwyay).

1 Like

Someone posted a STAHP sign here without an explanation. Is STAHP an acronym for something?

why don’t you ask your AI what it might mean

don’t forget to tell it that this word was displayed on a great honking big red octagon sign

3 Likes

I am using XAMPP on Windows with MariaDB (I guess its the default option) as local dev environment. I am with Bluehost so I checked and it looks like it supports MariaDB. So in terms of DB, I think I should be able to deploy it online as per Bluehost. As I mentioned before its my first time trying to build database driven website so I just googled some Stackoverflow explanation for

  • A BLOB (Binary Large Object) is used for storing raw binary data (like images, audio files, or compressed data) and is treated as a byte string with no character set or collation.

  • A TEXT type is used for storing large character strings (like articles or long descriptions) and has a specific character set and collation, meaning its values are sorted and compared based on character rules, not just numeric byte values

As you suggested its better to use TEXT and/or BLOB instead of VARCHAR. TEXT seems straightforward I guess I can just change all VARCHAR types to TEXT type in all the tables? But in terms of BLOB in which table/column would I use it?

It’s not necessarily recommended to change all VARCHARs to text; for smaller things, its perfectly fine. It’s just when you start getting to bigger numbers, and start asking yourself “Is this a string, or a text block?”

As you mentioned MariaDB; Maria is new enough that (to my knowledge) it’s always had a VARCHAR limit of 65532*. The database it roots from, MySQL, changed its VARCHAR limit from 255 to 65532* with version 5.0.3.

*: Subject to the limits of the row; because the row size is shared between all fields.

1 Like