User reporting Library - php

I would like to build (or find) a php reporting library that I can use to allow my users to report any type of content as bad. Actually, if they could report content as good (which I guess would mean a rating system) that would be even better. So if anyone knows of a library like this I would love to hear about it.
Anyway, this is how I imagine the database working and I would like to know if this sounds correct.
ID - ID of row
USER_ID - ID of the user voting/reporting
ITEM_ID - UNIQUE table row ID of the item reported (post, other user, link, etc...)
TYPE - the name of the table this pertains to (posts, users, links, etc...)
DATE - datetime of report
VALUE - I guess this could be an UP vote or DOWN vote
By including the [TYPE] column I can use this voting system across all content types on my site instead of just one (like forum posts). By storing the user_id I can be sure that only one vote/report is cast per item per user.

Well, I went ahead and built it like I said. Frank was right, it wasn't too hard. Here is the code incase anyone else wants to use it on a MVC that supports AR styled DB calls like CodeIgniter or MicroMVC:
/*
* Multi-table user voting model
*/
class votes extends model {
public static $table = 'votes';
function cast( $user_id = NULL, $item_id = NULL, $type = NULL, $vote = TRUE) {
$where = array(
'user_id' => $user_id,
'item_id' => $item_id,
'type' => $type
);
//Try to fetch this row
$row = $this->db->get_where(self::$table, $where );
//Look for an existing vote row
if( $row && ! empty($row[0]) ) {
//Fetch row
$row = $row[0];
//If they already voted this way... then we're done!
if( $row->vote == $vote) {
return;
}
//Else update the row to the new vote
$row->vote = $vote;
$row->date = convert_time(time());
$this->db->update( self::$table, $row, array('id' => $row->id) );
return;
}
//Else create a new vote for this item
$row = array(
'user_id' => $user_id,
'item_id' => $item_id,
'type' => $type,
'date' => convert_time(time()),
'vote' => $vote,
);
$this->db->insert( self::$table, $row);
}
}

Related

Matching node id with mysql table id

I have to make function where I can get node id from url (example: localhost/site/node-1)and make link to mysql databes row where is (example: id=1) and i need to do it dynamically with several nodes.. I try this code:
<?php
function module_name_menu() {
$items['node-%'] = array(
'title' => 'Row from database',
'page callback' => 'module_name_node_page_view',
'access callback' => 'node_access',
);
return $items;
}
function module_name_node_page_view(){
$id = $_GET['id'];
$results=db_query("SELECT * FROM csvupload WHERE id = $id");
$header = array ( t('First name'), t('Last name'), t('Email'));
$rows = array();
foreach($results as $result){
$rows[] = array(
$result -> first_name,
$result -> last_name,
$result -> email,
);
}
return theme('table', array('header' => $header, 'rows' => $rows));
}
When I go to localhost/site/node-1 I get Page not found. I need to do it with node-2, node-3... Can someone help me with this problem?
There are several ways to get current page node id, i.e.:
if (arg(0) == 'node') {
$nid = arg(1);
}
Or
if ($node = menu_get_object()) {
$nid = $node->nid;
}
Then, your format:
localhost/site/node-1
is not drupal's url format, but something custom for you. For that you can first use:
$_SERVER['REQUEST_URI']
to get your custom path. Then, since your path contains only one "-" sign you can use php explode() function to split by that sign and find string part right from the minus sign.
http://php.net/manual/en/function.explode.php
Also, using views is "more natural" ("drupalish") way for getting content from database, but if your assignment is specific...you have to do it that way. But if you have to load a node, then why are you selecting "csvupload" table instead? Drupal's database organization is a bit more complex than you expect. Not all fields you add to your content type are part of one table. It's more that every field you add is different table and you'll need a lot of joins to get the content you need. That's why it's better to use views...

Checking for Unique Key and updating row or creating a new one WordPress

I have a draggable div in which the position is being saved to database with user_id set to unique. How would I check if user_id exists.. and if it does, update the other 3 columns.. If it does not exist, insert new row. I have read to use "ON DUPLICATE KEY UPDATE", but having a hard time implementing it. Any thoughts?
As explained by #N.B. - Working solution
global $wpdb;
$_POST['user_id'];
$_POST['div_id'];
$_POST['x_pos'];
$_POST['y_pos'];
$user_id = $_POST['user_id'];
$div_id = $_POST['div_id'];
$x_pos = $_POST['x_pos'];
$y_pos = $_POST['y_pos'];
$wpdb->query($wpdb->prepare(" INSERT INTO coords
(user_id, div_id, x_pos, y_pos)
VALUES (%d, %s, %d, %d)
ON DUPLICATE KEY UPDATE
x_pos = VALUES(x_pos), y_pos = VALUES(y_pos)",
$user_id,
$div_id,
$x_pos,
$y_pos
));
As #N.B. pointed out in the comments, while the first method I submitted works, it is open to race conditions. I'd like to thank him for pointing that out. Here is that first solution with race conditions:
$user_exists = $wpdb->get_var(
$wpdb->prepare("SELECT user_id FROM coords WHERE user_id = %d", $user_id)
);
if($user_exists) {
// Do Update
}
else {
// Do Insert
}
These race conditions are astronomical, as an insert must finish executing in the time between the first query returning and the next insert query. But the race condition exists none-the-less, and therefore could happen at some point in time.
However your database is still safe. When it occurs it won't duplicate the data, but rather it will throw a wpdb error that a unique key already exists and the insert will silently fail (it won't terminate the script and it won't output the error unless error reporting is turned on).
WordPress database error: [Duplicate entry '3' for key 'PRIMARY']
Amazingly, the above technique is used in the Wordpress core and by countless plugin and theme authors, and I could only find two instances of 'ON DUPLICATE' being used correctly in the Wordpress core. So a large chunk of the internet runs with multiple instances of this race condition seemingly just fine, just to give you an idea of the astronomical chance we're talking about.
Regardless of the chance, to use it is bad practice. As N.B. commented, the database should worry about the data and not PHP.
The Real Solution
Wordpress, for whatever reason, does not have an 'INSERT ON DUPLICATE UPDATE' function, which means you have to either write up a query each time with $wpdb->query or build your own function to handle it. I went with writing a function because writing wpdb->query each time is a pain and brings the user one layer closer to accidental mysql injection. Also development speed.
/**
* Insert on Duplicate Key Update.
*
* Wordpress does not have an 'insert on duplicate key update' function, which
* forces user's to create their own or write standard queries. As writing
* queries is annoying and open to mysql injection via human error, this function
* automates this custom query in an indentical fashion to the core wpdb functions.
* Source: http://stackoverflow.com/a/31150317/4248167
*
* #global wpdb $wpdb
* #param string $table The table you wish to update.
* #param array $data The row you wish to update or insert, using a field => value associative array.
* #param array $where The unique keys that you want to update at or have inserted, also a field => value array.
* #param array $data_formats Wordpress formatting array for the data. Will default to %s for every field.
* #param array $where_formats Wordpress formatting array for the where. Will default to %s for every field.
* #return boolean True if successfully inserted or updated the row.
*/
function insertOrUpdate($table, $data, $where, $data_formats = array(), $where_formats = array())
{
if(!empty($data) && !empty($where)) {
global $wpdb;
// Data Formats - making sure they match up with the data.
$default_data_format = (isset($data_formats[0])) ? $data_formats[0] : '%s';
$data_formats = array_pad($data_formats, count($data), $default_data_format);
$data_formats = array_splice($data_formats, 0, count($data));
// Where Formats - making sure they match up with the where data.
$default_where_format = (isset($where_formats[0])) ? $where_formats[0] : '%s';
$where_formats = array_pad($where_formats, count($where), $default_where_format);
$where_formats = array_splice($where_formats, 0, count($where));
// Get Fields
$data_fields = array_keys($data);
$where_fields = array_keys($where);
// Create Query
$query =
"INSERT INTO $table" .
" (" . implode(', ', array_merge($data_fields, $where_fields)) . ")" .
" VALUES(" . implode(', ', array_merge($data_formats, $where_formats)) . ")" .
" ON DUPLICATE KEY UPDATE";
// Compile update fields and add to query
$field_strings = array();
foreach($data_fields as $index => $data_field) {
$field_strings[] = " $data_field = " . $data_formats[$index];
}
$query .= implode(', ', $field_strings);
// Put it all together - returns true on successful update or insert
// and false on failure or if the row already matches the data.
return !!$wpdb->query(
$wpdb->prepare(
$query,
array_merge(
array_merge(
array_values($data),
array_values($where)
),
array_values($data)
)
)
);
}
return false;
}
To use it, you simply enter parameters just like you would with a $wpdb->update function call.
insertOrUpdate(
'testing_table',
array('column_a' => 'hello', 'column_b' => 'world'),
array('id' => 3),
array('%s', '%s'),
array('%d')
);
In your case, it would be:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id),
array('%d', '%d', '%d'),
array('%d')
);
Or you can just use a default formatting:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id),
array('%d')
);
Or you can ignore the formatting which will default to formatting as strings:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id)
);
If you find any issues just let me know.

Cakephp Validate uniqueness of name field w.r.t other field of same table

I am a kind of noob in cakephp and While working with this validation rule`, I were unlucky to get the satisfactory response.
In my project, I have to valiidate the name w.r.t foreign key and many other attribute like is_active
Lets Say
Table t1 has attributes (id, name varchar, is_active Boolean)
table t2 has attribute (id, name varchar, t1_id (foreign key to table t1), is_active boolean)
Now I want to validate unique name of table t1 of is_active group
And validate uniqueness of t2.name w.r.t is_active=1 and t1_id=specific_value
I googled and found a link regarding this with no luck :(
http://www.dereuromark.de/2011/10/07/maximum-power-for-your-validation-rules/
Any help will really be appreciated.
First some suggestions that may be worth considering;
Soft delete
Apparently you're trying to implement a 'soft delete' in your website. Soft-deletes are sometimes wanted in case you want to delete something, but being able to 'undelete' it in a later stage.
However, by allowing both active and inactive items to share the same name, 'name' is no longer unique (based on your question, unique names are a requirement).
This will prevent you from undeleting an item, because at that point, two items with the same name exist in your database, both active.
Here are some discussions on the subject of 'soft deletes';
Are soft deletes a good idea?
http://richarddingwall.name/2009/11/20/the-trouble-with-soft-delete/
Revisions/History
If you're trying to implement a 'revision history', not a 'soft delete', it's best to store revisions in a separate table. There are already some plugins for CakePHP that may handle this for you. I don't have links to them, but you'll be able to Google for that.
Custom validation
Back to your question; you are able to check if a record is unique within the 'active' group by creating your own validation-rule inside the Model, for example:
public function isNameUniqueActive()
{
if (
array_key_exists('is_active', $this->data[$this->alias])
&& 1 != $this->data[$this->alias]['is_active']
) {
// Record to save is not 'active', so no need to check if it is unique
return true;
}
$conditions = array(
$this->alias . '.name' => $this->data[$this->alias]['name'],
$this->alias . '.is_active' => 1,
);
if ($this->id) {
// Updating an existing record: don't count the record *itself*
$conditions[] = array(
'NOT' => array($this->alias . '.' . $this->primaryKey => $this->id)
);
}
return (0 === $this->find('count', array('conditions' => $conditions)));
}
You'll be able to use this validation just like the built-in validation rules;
public $validate = array(
'name' => 'isNameUniqueActive',
);
update
To also check if a name is unique within a group (based on foreign-key t1_id), try this:
public function isNameUniqueWithinGroup()
{
if (
array_key_exists('is_active', $this->data[$this->alias])
&& 1 != $this->data[$this->alias]['is_active']
) {
// Record to save is not 'active', so no need to check if it is unique
return true;
}
if (!array_key_exists('t1_id', $this->data[$this->alias])) {
// No group specified - not valid?
return false;
}
$conditions = array(
$this->alias . '.name' => $this->data[$this->alias]['name'],
$this->alias . '.is_active' => 1,
$this->alias . '.t1_id' => $this->data[$this->alias]['t1_id'],
);
if ($this->id) {
// Updating an existing record: don't count the record *itself*
$conditions[] = array(
'NOT' => array($this->alias . '.' . $this->primaryKey => $this->id)
);
}
return (0 === $this->find('count', array('conditions' => $conditions)));
}

Ordering an Array with weights?

The problem
Right now I have to create this:
In my database; each favorite as its own 'weight' entry.
As of right now it defaults to '10'
Let's say I have an array of favorites,
the weights go like this accordingly:
1
2
3
4
If I were to press the 'down' arrow on '2' I'd have to add +1 to its weight.
Which would result in:
1
3
3
4
But I just want to switch favorite '2' with favorite '3's weight.
I need a way to ensure that when the weights for each item function as they're implied.
Since just adding or subtracting 1 will lead to a lot of problem.
Hopefully I explained it properly.
The code
Writing to the database
/*
* Write Form data to database
*/
function f25_favorites_form_submit($form, &$form_state){
global $user;
$listOfPaths = f25_favorites_listOfPaths();
$selected = $form_state['values']['path'];
$data = array(
'uid' => $user->uid,
'path' => $selected,
'title' => $listOfPaths[$selected]['#title'],
'weight' => 10,
'timestamp' => time(),
);
drupal_write_record(f25_favorites, $data);
}
The Query
/*
* Fetching Specific User's Favorites Data
*/
function f25_favorites_user_favorites() {
global $user;
if($user->uid > 0){
$userid = $user->uid;
$user_paths = db_query("SELECT * FROM f25_favorites foo WHERE foo.uid = '%s' ORDER BY foo.weight DESC", $userid);
$pathlist = array();
while ($data = db_fetch_object($user_paths)) {
$pathlist[] = $data;
}
return $pathlist;
}
}
How should I approach this problem?
if all your favorites are weighted differently, you can simply swap the weight field of the one "getting up" with the one "getting down", or reverse, when decreasing weight. you'd have to figure out the code though
as there are only little items I would save the whole order to the database, not only what has been moved.: i don't know much about drupal but i think it would be easy to accomplish as drupal is php and javascript based.
each element in html has its own id/index (uid?)
<div id="el_1">Menu1</div>
<div id="el_2">Menu2</div>
use javascript to get list of your items in the reordered order (like:
itm[0]=item1;
itm[1]=item3;
itm[2]=item2;
itm[3]=item4;
send itm with ajax to your php updater. and update your db, use the position received and not weights.
now your db has something like this:
uid;position
1;0
3;1
2;2
4;3
I recommend you jquery for the javascript work, jquery has a nice sortable plugin. but it is also awesome for the ajax stuf.

Cakephp saving 10 new rows after checking id of the user

I'm trying to create 10 new rows in controller if the userid is not found in the user_id row. I tried create() function and then save() function but it doesn't seem to do the job.
Below is the code, is there a way we can solve this issue?
function invite_fellows(){
//Read userid
$userid = $this->Session->read('Auth.User.id');
$invite_table = $this->User->Invite->findbyUserId($userid);
if(empty($invite_table)){
$code_limit = 10;
//Save 10 unique codes for the user
for($i=0; $i<$code_limit;$i++){
$unique_id = $this->_unique($userid); // Unique id with userid as initial prefix
$this->data['Invite'] = array('user_id' => $userid, 'code' => $unique_id);
$this->User->Invite->create();
$this->User->Invite->save($this->data['Invite']);
}
}
//Find user in users and associated tables
$user = $this->User->findbyId($userid);
//Find user in invite table
$confirmed = $this->User->Invite->find('count', array('conditions' => array('user_id' => $userid,'invited_user >' => 0)));
$this->set(compact('user','confirmed'));
}
Thank you.
Most likely there's a validation rule that blocks the save. Try adding debug( $this->User->Invite->validationErrors ); after the save() to check for that. Make sure debug level is set to at least 1 in core.php.

Categories