I'm in the planning stages for a web-based project management/collaboration app similar to Copper Project or PHP Collab, using concrete5 as my framework.
There are a couple of features I want to integrate, but I'm not entirely sure how to accomplish this, looking at how DB tables are generated with blocks.
The functionality I have in mind is as follows:
1) When a new client is created by an account manager or project manager, they have to assign a three-character prefix for the client. Example: if (by some wild stroke of luck) I add Diesel as a client, I would want to assign them the prefix DSL.
2) When an account manager or project manager creates a new project, the project ID should be directly related to the client, and not to the total number of projects for all clients. In other words, the project ID for Diesel's first project with me should be DSL001, and not DSL016, because there were fifteen other projects for other clients before this one (c.f. both Copper and PHP Collab, which follow the global project ID logic, as opposed to the per-client project ID logic). This project ID would be visible on the front-end project page that's been created by the AM/PM, and would also be used as a reference ID for things like cost estimates, invoices and so on.
So this is where I run into a problem from a workflow planning point of view. My understanding of MySQL is such that if I want to follow my own project ID logic, a new table would have to be created for each and every client, to contain all of the data concerning their projects, so that the DB could correctly output the unique ID number.
However, my understanding of C5 is that if, for example, in the course of creating this app, I decide to create the project form as a block to be inserted in a front-end template, the db.xml file would create a generic project data table in the DB for all clients, not one per client.
Any suggestions how I can accomplish what I'm looking to do in the context of C5's framework?
If something's unclear, I can show some mock-ups of how a project page would look.
Thanks!
This is a general database schema issue, and has nothing to do with Concrete5 specifically. Your idea about needing a separate table for each client just so MySQL could generate unique ID numbers is way off.
There is a general principle with database schemas that says the "ID" number of a record should only be used to uniquely identify records internally (within your application and database code) -- you should almost never use the primary id numbers for actual "business logic". In your case, you have a project id that has both letters and numbers in it, so even if you wanted to use the MySQL-generated ID for this, you couldn't (because those id's are integer numbers only, not letters).
Also, creating separate tables for the same kind of data is the exact opposite of how databases work. Instead what you want to do is have one table for clients and another for projects. The client table would have an "id" field (the auto-increment number), and a client prefix field (the "DSL" in your example). Then the projects table has its own "id" field (again, the auto-increment number), and a "client id" which ties that project to a record in the client table. Then you'd have another field in the projects table for "project number". This project number field is what you'd display to users (you'd combine it with the client's 3-letter prefix -- so really you are storing two separate values in the database, but your users would see just one combined value because that is how you would output it to the page).
This "project number" field should not be an auto-increment number, because as you've discovered, MySQL only has one numbering sequence per table. So instead you will have some code in your application somewhere that generates this number for you when you have a new project. That code would be something like this:
function save_new_project($client_id, $project_data) {
$db = Loader::db();
//Determine the highest existing project number for this client
$sql = "SELECT MAX(project_number) FROM projects WHERE client_id = ?";
$vals = array($client_id);
$max_project_number = $db->GetOne($sql, $vals);
if (empty($max_project_number)) {
$max_project_number = 0; //first project for this client
}
//Insert new project with next-highest number
$new_project_number = $max_project_number + 1;
$sql = "INSERT INTO projects (client_id, project_number, some_field, another_field) VALUES (?, ?, ?, ?)";
$vals = array($client_id, $new_project_number, $project_data['some_field'], $project_data['another_field']);
$db->Execute($sql, $vals);
}
By the way, Concrete5 is probably not a good framework to use for this kind of project. You might want to look into a more general framework that is suitable for web applications such as CodeIgniter, Symfony, CakePHP, Kohana, etc.
Related
What is the best-practice for maintaining the integrity of linked data entities on update?
My scenario
I have two entities "Client and
Invoice". [client is definition and
Invoice is transaction].
After issuing many invoices to the
client it happens that the client
information needs to be changed
e.g. "his billing address/location
changed or business name ... etc".
It's normal that the users must be
able to update the client
information to keep the integrity of
the data in the system.
In the invoice "transaction entity"
I don't store just the client id but
also all the client information related to the
invoice like "client name, address,
contact", and that's well known
approach for storing data in
transaction entities.
If the user created a new invoice the
new client information will be
stored in the invoice record along
with the same client-id (very
obvious!).
My Questions
Is it okay to bind the data entities
"clients" from different locations
for the Insert and the update?
[Explanation: if I followed the
approach from step 1-4 I have to
bind the client entity from the
client table in case of creating new
invoice but in case of
updating/printing the invoice I have
to bind the client entity from the
invoice table otherwise the data
won't be consistent or integer...So
how I can keep the data integrity
without creating spaghetti code in
the DAL to handle this custom
requirements of data binding??]
I passed through a system that was
saving all previous versions of an
entity data before the update
"keeping history of all versions".
If I want to use the same method to
avoid the custom binding how I can
do this in term of database design
"Using MYSQL"? [Explanation: some
invoices created with version 1.0 of
the client then the client info
updated and its version became 1.1
and new invoices created with last
version...So is it good to follow
this methodology? and how I should
design my entities/tables to fulfil the requirements of entity
versioning and binding?
Please provide any book or reference
that can kick me in the right
direction?
Thanks,
What you need to do is leave the table the way it is. You are correct, you should be storing the customer information in the invoice for history of where the items were shipped to. When it changes, you should NOT update this information except for any invoices which have not yet been shipped. To maintain this type of information, you need a trigger on the customer table that looks for invoices that have not been shippe and updates those addresses automatically.
If you want to save historical versions of the client information, the correct process is to create an audit table and populate it through a trigger.
Data integrity in this case is simply through a foreign key to the customer id. The id itself should not ever change or be allowed to change by the user and should be a surrogate number such as an integer. Becasue you should not be changing the address information in the actual invoice (unless it has not been shipped in which case you had better change it or the product will be shipped to the wrong place), this is sufficent to maintain data integrity. This also allows you to see where the stuff was actually shipped but still look up the current info about the client through the use of the foreign key.
If you have clients that change (compaies bought by other companies), you can either run a process onthe server to update the customer id of old records or create a table structure that show which client ids belong to a current parent id. The first is easier to do if you aren;t talking about changing millions of records.
"This is a business case where data mnust be denormalized to preserve historical records of what was shipped where. His design is not incorrect."
Sorry for adding this as a new response, but the "add comment" button still doesn't show.
"His design" is indeed not incorrect ... because it is normalized !!!
It is normalized because it is not at all times true that the address corresponding to an invoice functionally depends on the customer ID exclusively.
So : normalization, yes I do think so. Not that normalization is the only issue involved here.
I'm not completely clear on what you are getting at, but I think you want to read up on normalization, available in many books on relational databases and SQL. I think what you will end up with is two tables connected by a foreign key, but perhaps some soul-searching per previous sentence will help you clarify your thoughts.
I have 3 tables called PATIENT, PHONE and PATIENT_PHONE.
The PATIENT table contains the columns: id, firstname, lastname, email and dob.
The PHONE table contains the columns: id, type and number.
The PATIENT_PHONE table contains the columns: patient_id, phone_id.
The PATIENT and PHONE tables are mapped by the PATIENT_PHONE table. So I have to join these 3 tables to post firstname, lastname, email and number fields to the database.
I tried like this:
Schema for first_xmlmap
[
Schema mapping for Patient and Patient_phone
[
I'm assuming you want to write the same data to multiple database tables within the same database instance for each request against the web service.
How about using the tHashOutput and tHashInput components?
If you can't see the tHash* components in your component Pallete, go to:
File > Edit project properties > Designer > Pallete settings...
Highlight the filtered components, click the arrow to move them out of the filter and click OK.
The tHash components allow you to push some data to memory in order to read it back later. Be aware that this data is written to volatile memory (RAM) and will be lost once the JVM exits.
Ensure that "append" in the tHashOutput component is unchecked and that the tHashInput components are set not to clear their cache after reading.
You can see some simple error handling written into my example which guarantees that a client will always get some sort of response from the service, even when something goes wrong when processing the request.
Also note that writing to the database tables is an all-or-nothing transaction - that is, the service will only write data to all the specified tables when there are no errors whilst processing the request.
Hopefully this gives you enough of an idea about how to extend such functionality to your own implementation.
I'm not a coder, but from time to time I have to interact with out client database. The company I work for does online education. The problem is that we have two SQL databases
Client_personal
client_educational
Client personal keeps all of the name, email, address, phone type of information. Client educational keeps track of what classes they have purchased. I need to take the information from client educational and tag the client personal information in our CRM. The problem is that the the two databases only have one common field "client id" and my CRM only allows me to search for duplicates by client name. So basically I need to add the client name column to my client educational database. I've added it, but it just says null. Anybody have any advise?
Your question is a little vague but assuming:
They're on the same machine
The table is called clients in both databases
The "client name" field is called clientName in both databases
The "client id" field is called clientID in both databases
You want to copy the data from one table into the other as a one off
You can use a user account with access to both databases
As always please don't run this on your real data. Try a mock first.
something like:
UPDATE client_educational.clients
SET client_educational.clients.clientName = Client_personal.clients.clientName
FROM client_educational.clients
INNER JOIN Client_personal.clients
ON client_educational.clients.clientID = Client_personal.clients.clientID
If you don't actually want to copy data as a one off, you should create a view or use join statements instead.
I am a developer and have never worked on DB before (designing a DB). I am designing a database for an employee management system which is a Node.js + Express application using MySQL as its DB.
I already have the required tables, columns sorted out but there are still few unknowns I am dealing with. This is my plan so far and I need your input on it.
The end users using this application will be small - mid size companies. The companies won't be sharing the tables in the database. So if there is a table named EmployeeCases I plan to create a new EmployeeCases table for each existing company or a new one who signs up for this application. I am planning to name the table as EmployeeCases_989809890 , where "989809890" will be the company id (or customer id). So if we have 3-4 companies who signed up for us, then all the tables (at least the ones which a company uses) will be recreated and named as TableName_CompanyId. My questions, is this a good way to go? Is there a better way?
All the employee's data is held by the Employee table, including their login and password. Now each Employee table in DB will be named as Employee_CompanyId (as per my plan above). My question is, when an employee logs in, how will I know which Employee table to query to? Or should I remove the login from the Employee table and create a universal Users table where all the employees will be stored? The Users table will also have the CompanyId as one of its column and I will read the CompanyId from there which will be used to query other tables.
Any reference, website or blogs on this type of design will be appreciated.
Thanks.
I don't recommend this approach, I think you should either:
A) Put all the information in the same tables and have a companyId column to sort them out
OR
B) Have separate databases for each company and use the appropriate database using the code.
The thing is, with your approach, you'll have a hard time maintaining your application if you have multiple copies of the same table with different names. If you decide to add a column to one of the tables, for instance, you will have to write as many SQL scripts as you have table instances. You'll also have a bad time with all of your unique identifiers.
Here are some advantages/disadvantages of each design:
A) Put all the information in the same tables and have a compagnyId column to sort them out
Advantages:
Simplest
Allow usage of foreign key / constraints
Great for cross / client data extraction
Disadvantages:
Not portable (a client can't just leave with his/her data)
Can be perceived as less secure (I guess you can make the case both ways)
More likely to have huge tables
Does not scale very well
B) Have separate databases for each company and use the appropriate database using the code.
Advantages:
Portable
Can be perceived as more secure
Disadvantages:
Needs more discipline to keep track of all the databases
Needs a good segregation of what's part of your HUB (Your application that tracks which client access which database) and that's part of your client's database.
You need a login page by company (or have your clients specify the company in a field)
An example of an application that uses this "two-step login" is Slack, when you sign-in you first enter your team domain THEN your user credentials.
I think Google Apps for Work as the same approach. Also, I think most CRM I worked with has a separate database for their clients.
Lastly, I'd like to direct you to this other question on stackoverflow that links to an interesting example.
You shouldn't split your tables just because companies won't share their information. Instead, you should have a companyId column in each table and access to the relevant data for each query. This should be implemented in your backend
I have two tables, applications and applicationRevisions, structured:
applications (id, time, user, views)
applicationRevisions(id, application, time, user, name, description)
The latter holds 'revisions' of an application page, containing title and description, so it's a one-to-many relationship. When an application page is shown, the latest applicationRevisions row with the matching application ID is chosen.
However, I have no way of knowing if an application with a certain name exists at any particular time because previous revisions may have different names, and the name is not stored in the applications table.
I have a workarounds; store the current name as a field in applications, and when an application is edited, add the row as usual to applicationRevisions, but then update the name in the applications row.
Is there a better way to do this?
So, you'd like to search for a name in to get the application and then get the most recent revision of that application, which may no longer have that name. This is certainly possible with subqueries but what are you going to do about applications which happen to have the same name for some revisions?
Would be much more clear if the application table could hold the name and description. To be honest, this could just be a single table since time and user in application would likely just be the same as used for the first revision of each application. Only the views field is left and that could be in a table of just applications_views (application, views).
Anyway, if you want to avoid major changes to the schema and the name confusion between applications is OK, you could make a query something like this:
select * from applications join applicationRevisions
on (applications.id=applicationRevisions.application)
where applicationRevisions.id in
(select max(id)
from applicationRevisions
where name = 'foobar'
group by application);
If I guessed the relationships correctly, this will give you all fields from the most recent revision of each application that ever used the name 'foobar'.
Correct me If I am missing something here, every application entry must atleast have a applicationRevision right ?
Why not use foreign key constraints ? Make application field of the applicationRevision table a foreign key. Identify application with a id and not a name. Make name the property of the revision.
So lets say you want to search for a application which has a name "wxyz", so you do a
select id,application from applicationRevision where name="wxyz" order by time DESC LIMIT 1;
This gives you the application id. You can do a JOIN and get application fields from a single query