(Cake)PHP - Updating child databases with data from master - php

I'm developing an application using CakePHP (latest version) framework for PHP that will unite several instalations of a CMS with a single registration form. I'm using a MySql DB. The problem is that I cannot modify the CMS code, I can only modify the DB data since very frequent CMS updates will overwrite my modifications.
The idea is that the system will keep a master Users table as shown in the diagram with the shared data. The table model of the master Users table and the child Users table is the same, except for a few CMS installation specific columns (as shown in the simplified diagram).
Now I'm trying to figure out a way to reflect the changes I do on master (INSERT and UPDATE) to occur on all of the child DBs. So when I make a change in my CakePHP app I want those fields that are shared to be updated in all child DB instances.
One solution would be to write manual MySQL queries for every single action in the application but that seems redundant and annoying.
How could I make my app to repeat all of the queries I do on my MASTER on every CHILD. Basicly if I UPDATE the name of entry ID 5 in MASTER, that that same query is ran on all of the child databases (which will hold the same entry under the same ID but with a few additional columns).

You could do that easily with CakePHP's model triggers (beforeSave, afterSave...) BUT keeping all that stuff synchronized will soon become your worst nightmare. Instead, I would do something like this:
Master.User has (id, name, password, specData1, specData2...)
Child1.User is a mysql VIEW: CREATE VIEW User AS SELECT id AS id, name AS name.., specData1 AS specData FROM Master.User
Yes, you can create cross-DB views. For the CMSs Child1.User is a table, all operations CREATE/DELETE/UPDATE are transparently done by the CMS on the VIEW thinking its a table. (but Master.User gets updated)

Related

Pre-audit system with Postgres?

How to implement pre-audit system for a web-app? I mean when the data changes made by regular users are saved for review and only after that they can be applied (or rejected) to main tables.
How to organize and keep in sync relations between data table and preliminary table, when the table name is changed for example, or worse, table structure?
How to validate submitted data for constraints?
Is there something similar that I can use, or read about?

Best way to map a mysql table to new table with different field names

I'm facing a migration problem on the job. Right now we have a single database which is used by our website and by a backoffice tool.
As part of trying to improve our code in the website, we've desgined a new schema for several tables in the database.
As an example let's take the products table. It's fairly standard with fields like description, name, product code, price, creation date etc. We now have a new table, let's call it better_products. The problem is, we can change the website code all we want, but we can't touch the backoffice tool's code which relies heavily on the old products table.
We're going to end up in a situation in production where the backoffice tool is writing to the old products table, and the website is reading from the new better_products table. The question is, how do we keep both of them in sync? I've been googling around for some time now, and by far the most common solution is to use triggers, and map the incoming data to the new table. I've written the AFTER INSERT trigger for the products, but when I went to write the UPDATE trigger it turned out there's no way to iterate over the fields that changed inside the trigger and map them over. This means writing out the fields by hand a la 'IF NEW.fieldName <> OLD.fieldName THEN' which is ugly and requires listing the fields out by hand.
Is there a better way? a semi-better way? anything except writing this out field by field?
Is there a better practice than using triggers?
Please don't suggest changing the backoffice tool as this is not a realistic option right now. It's planned, but not soon enough for us to be able to wait for it.
Create a view in the mysql database called better_products that is a select statement on the old product but with aliases for the column names that have changed.
Eventually, you can update the code in the backoffice app, to use this view. Once both systems are using the new view, that view can be replaced by an actual table called better_products that has all the data from the old table copied over.

Yii application log littered with COUNT(*)

When running SELECT queries it seems as if Yii is often performing each one twice. The first is a COUNT() and the second is the actual query.
What is causing this? It seems terribly inefficient.
In a related note, why does Yii perform a SHOW COLUMNS FROM and SHOW CREATE TABLE so often? Doesn't setting up a relation within the Model tell Yii enough about the schema?
I assume you are using active records a lot in conjunction with listing widgets such as CGridView and CListView.
What is causing this? It seems terribly inefficient.
Well, in order for the pagination to work in CListView and CGridView, the assigned CActiveDataProvider (or actually any data provider) needs to fetch the total item count. This won't work with the result set which usually has a LIMIT clause applied. Hence, an additional COUNT() is performed to retrieve said number.
In a related note, why does Yii perform a SHOW COLUMNS FROM and SHOW CREATE TABLE so often? Doesn't setting up a relation within the Model tell Yii enough about the schema?
No. Yii does far more than managing related models. Part of the AR abstraction layer is also to determine which fields are available in a table and hence can be accessed on a model representing a table row. However, you don't have to live with this as schemata can be cached conveniently. To do so, follow these steps:
Configure a caching component such as CApcCache in your protected/config/main.php in the components stanza.
Change the configuration of your db component so it contains the following lines:
'schemaCacheId'=>'cache', // This is the name of the cache component you
// configured in step 1. It's also the default value.
'schemaCacheDuration'=>3600, // Cache table schemata for an hour.
// Set this higher if you like.
A word of advice; don't do this in your development environment: If your database design changes, AR models might not reflect this due to stale caches.

Different table prefix for one table?

Hypothetically, let's say I had multiple installations of some odd MySQL/PHP driven software. They are the same software so the database table structure is the same cross all of the installs. What I want to do, is make the software use a different database prefix for just one table. Specifically, a user table. So say the installs are set up like this:
Main install: /home/www/main, database main, prefix is1
Second install: /home/www/second, database main, prefix is2
Third install: /home/www/third, database main, prefix is3
So what I want is to tell second install and third install to pull from the users table on prefix is1 for its own data via that table. Thus, any user that registers on main install is also registered on second and third and vice versa. Is it possible, and how would I do it if it is? Even if it's just a workaround that gives the same basic result I would be happy.
If you don't want to modify the app's PHP source-code, and it's not already configurable in this respect, then another option is to modify the database, changing is2users and is3users to be views on is1users:
DROP TABLE is2users;
CREATE VIEW is2users AS SELECT * FROM is1users;
DROP TABLE is3users;
CREATE VIEW is3users AS SELECT * FROM is1users;
(See http://dev.mysql.com/doc/refman/5.0/en/views.html for general information on views, http://dev.mysql.com/doc/refman/5.0/en/create-view.html for information on CREATE VIEW specifically.)
Depending on the app, this may not work perfectly -- for example, the app might cache some information in memory (such as the current value of an identifier-sequence) -- but it will probably work. Test it before putting it in production!
Your php code likely goes something like this in something like cfg.php:
$prefix = 'is3'
and in something like user.model.php:
$sql = 'SELECT * FROM `'.$prefix.'users`';
So you need to change in two of three installs code for working with 'users' table. But it seems to be too dangerous.
The setup of this is easy, schema-wise. You mention 'installs' which means you are using some packaged library which probably contains a config file where you can change various settings, and chances are one of the settings is the table prefix. If there is no table prefix option you can browse the install code and find where the schema is located and change the prefix manually for each install.
Now comes the hardpart, getting the code to behave as you described. You will have to make your app aware of all three databases, meaning you will probably have to add two new database connectors to the code (one connector for each database). Then you will have to add logic to handle user CRUD functionality to insert/update/delete user data across all three databases (transaction would be good here).
What you are describing is possible, but will require some custom code.

Simple versioning (in PHP/Zend Framework)?

i am wondering how can i have simple versioning, say i want to be able to undo past edits (just up to 3 will do) to a post. maybe because the app allows other user to edit a post. but i may want the owner to restore the post for example.
do i just store the full post in another field?
One suggestion could be something like this:
you propably have each post as a separate row in the table and it has indexes that point to right threat and you retain their order by those indexes.
Add a "revision" to each post row. Everytime user edits the post, you store a new row into table but with higher revision. And when showing the posts, you just fetch the one with highest revision..
I've done this previously by creating a table which contains the following columns:
id, serialisation value of entire row you want backed up, the table it is for, datetime, why the backup was created.
That way you have a complete listing of all the versions of that table. I use Doctrine ORM so that way I can setup a postSave hook to create a new backup version in that table.
Doctrine Versionable Bebaviour may be the simplest solution for you:
Doctrine - Doctrine ORM for PHP - Versionable

Categories