How can I use two different tables for one entity in Symfony?
I have an entity calles 'Article' which represents a blog article.
It consists of the following properties:
Article ID
Title
Author
Text
Date
Views
I remember reading that a table cache is somewhat destroyed when something is written to it. That's why I'd like to have a separate table containing only the article ID and the number of views.
You can have a table article_views with the article_id as a FK and visits counter.
You an update it "on-the-fly" with Propel and symfony 1.4 by a peer method in it's peer class on each visit by giving the article id and connection.
public static function incrementVisits($id, PropelPDO $con = null)
{
$con = (is_null($con))
? Propel::getConnection(self::DATABASE_NAME, Propel::CONNECTION_WRITE)
: $con;
$sql = sprintf('UPDATE %s SET %s = %s + 1 WHERE %s = %d',
self::TABLE_NAME, self::VIEWS, self::VIEWS,
self::ARTICLE_ID, $id);
$stmt = $con->prepare($sql);
return $stmt->execute();
}
You can wrap that login in a method in Article:
public function incrementViews(PropelPDO $con = null)
{
return ArticleView::incrementVisits($this->getId(), $con);
}
Then you can implement something like that in a method in Article:
public function getViews(PropelPDO $con = null)
{
return $this->getArticleView($con)->getViews();
}
Another possible solution for offline/delayed processing is to use a message queueing system and put a message there with the id of the article every visit. Then on a given interval retrieve the messages and update the table with a method similar to the one above but with variable for the new visits you would like to add.
This option will make the UPDATE queries load more predictable, but will introduce some delay.
Hope this helps. I guess the table cache you are referring is the cache for the SELECT on MySQL.
Related
i was getting in a question when i got this scenario:
I have to make a history log about what the user does and of course the user can do a lots different action.
i thought two different 2 way for make it i just need someone that can help me to follow the right way.
First way:
Create 2 different tables
History_user
History_type
History_user table
id | user_id | history_type (int)
1 1 1
1 3 2
History_type
id | name_action (string)
1 The user has posted on the wall
2 The user has change his profile picture
and then just join on the query with History_user.history_type = History_type.id
Second way:
is create the History_user table and an helper example called Converter.
<?php
class Converter {
function history($type_history) {
switch($type_history) {
case 1:
$human_history = "The user has posted on the wall";
break;
case 2:
$human_history = "The user has change his profile picture";
break;
}
return $human_history;
}
}
$converter = new Converter();
$converter->history(1);
I was looking for the better way for do that, in terms of performance and maintainability. Thank you.
Both helper and History_type table are necessary for information representation. In terms of performance it doesn't really matter, because you will insert only in one table on user action. If you need to represent data, you will need just one more query to get descriptions of actions (without joins, ofc, if you want some performance). So 2 tables way is more flexible and extendable.
You still could do that helper function which lets say will have static cache variable - array of id => name of actions, which will be lazy loaded on history() function like this:
class Converter {
protected static $_cache;
protected static function _loadCache() {
if (null !== self::$_cache) {
return;
}
self::$_cache = array();
$query = "SELECT * FROM `History_type`";
$res = mysql_query($query);
while ($row = mysql_fetch_assoc($res)) {
self::$_cache[(int) $row['id']] = $row['action'];
}
}
public static function history($id) {
self::_loadCache();
return isset(self::$_cache[$id]) ? self::$_cache[$id] : 'Undefined action';
}
}
Converter::history(1);
I'm working on a project that has one main article and you can attach many different articles to it. So on submit I have broken the article from into two different sections, since I need them to submit into two different tables.
After submitting the first part, I'm trying to get the last submitted id based on their user_name so I can attach the rest of the article. If that makes sense.
I've tried several different things, but nothing seems to grab back that id.
First I tried the insert_id() meathod, but that returns a 0
public function pullLastStoryId($author){
$this->db->where('author', $author);
$this->db->insert_id();
$story = $this->db->get('story_tbl');
return $story->result();
}
So then I also tried just grabbing
public function pullLastStoryId($author){
$story = $this->db->query('SELECT LAST_INSERT_ID() INTO story_tbl;')
return $story->result();
}
Any idea's
My guess is that you are talking about a foreign-key relationship between two tables. One way to do this would be something like:
$this->db->insert('author',$vals);
$id = $this->db->insert_id();
// do something with ID.
From what I can tell, you would probably then do something like this:
$story_row = $this->db->get_where('story_tbl',array('id' => $id));
You should not have to worry about different user_name property, because that will be last created ID. If you really wanted to get that row back, though, you could do this:
$author_row = $this->db->get_where('author',array('author_id' => $id));
If you need to have this ID somewhere else (say, after another form is submitted), you can either use a hidden form input or (this is a bit more secure), you can store that ID in session.
I am not clear with the question. Are you looking for something like this?
public function pullLastStoryId(){
$story = $this->db->query('SELECT id from tablename where id = LAST_INSERT_ID()')
return $story->result();
}
OR
public function pullLastStoryId(){
$id = $this->db->insert_id();
$this->db->where('id',$id);
$story = $this->db->get('story_tbl');
return $story->result();
}
If you want to get Last id, your way is wrong. $this->db->insert_id() or etc for active query. Try this: SELECT id FROM table ORDER BY id DESC LIMIT 1
I need to make a fundamental decision of my database/web interaction and I am missing the knowledge to even find proper search terms.
Background:
I am building a family website which supports a forum, family tree, pvp games with rankings and more details, all from a datamodel. Technologies right now: Php, MySQL, javascript in object oriented fashion.
Requirement:
In a forum datamodel, process a written post as addition of a new forum topic (thread).
Current approach:
In my current datamodel this would imply and update on two tables: Post and Topic.
I would need to insert a row in the topic table, then get the newly generated topicId(sequence), and then use that in an insert to the post table.
Problem:
I feel this is too much work for what needs to happen, too much interaction.
But it will become a typical requirement if I stick with the current approach.
Question:
am I on the right track anyway or should I
restructure the datamodel or
pick another way of database interaction (e.g. stored procedures)
am I facing a typical example where you would use methodology/framework xyz.
Currently tables have following structure (loosely based on this one from erdiagrams.com)
TOPIC: ('thread')
id
Forum_ID (FK)
Person_ID (FK)(threadcreator)
IsLocked
IsSticky
Subject
ViewCount
DateCreated
Tc_post_id - trigger to last post_id in this thread
POST
id
topic_id(FK)
person_id(FK)
subject
message
timestamp
replyto
Then I have a view that collects the last post for each topic and displays some info on that as well (e.g. last poster image) over the trigger Tc_post_id.
Ad 1 and 2: Your data model is fine. Using foreign keys is crucial here. One more thing that you need to take care of is that the database should ensure there is a TOPIC record for each POST. This is done by setting POST.topic_id NOT NULL attribute. This is sufficient safety mechanism on the DB side, as it ensures that no POST will be left without TOPIC. No matter what you do now with your POST you are obligated to provide a TOPIC.
Ad 3: A trigger with stored procedure is not recommended here as you have additional data in your TOPIC table (IsSticky, IsLocked, etc), which you might want to provide upon TOPIC record creation. Also, if such a trigger would be applicable, the database design would be a subject to denormalization.
Ad 4: On the business logic side you can now aid yourself by writing a automated mechanism to create the TOPIC record every time a new POST record is created without specified topic_id. I recommend using some ORM for this or take advantage of the data models available in any MVC framework. The blueprint for such models would look like this:
abstract class AModel // this class should be provided by ORM or framework
{
/**
* #var PDO
*/
protected $_db_driver;
public function getLastInsertId()
{
$stmt = $this->_db_driver->prepare('SELECT LAST_INSERT_ID() AS id');
$stmt->execute();
return $stmt->fetch(PDO::FETCH_OBJ)->id;
}
public abstract function getFieldList();
}
class ForumTopicModel extends AModel
{
public function insert(array $data)
{
$sql = 'INSERT INTO topic VALUES (:id, :forum_id, :person_id, :is_locked, ...)';
$stmt = $this->_db_driver->prepare($sql);
return $stmt->execute($data);
}
public function getFieldList()
{
return array('id', 'forum_id', 'person_id', 'is_locked', /*...*/);
}
// ...
}
class ForumPostModel extends AModel
{
public function insert(array $data)
{
$sql = 'INSERT INTO post VALUES (:id, :topic_id, :person_id, :subject, ...)';
$stmt = $this->_db_driver->prepare($sql);
return $stmt->execute($data);
}
public function getFieldList()
{
return array('id', 'topic_id', 'person_id', 'subject', /*...*/);
}
public function insertInitialTopicPost(array $form_data)
{
$this->_db_driver->beginTransaction();
$result = true;
if ( empty($form_data['topic_id']) ) {
// no topic_id provided, so create new one:
$topic = new ForumTopicModel();
$topic_data = array_intersect_key(
$form_data, array_flip($topic->getFieldList())
);
$result = $topic->insert($topic_data);
$form_data['topic_id'] = $topic->getLastInsertId();
}
if ( $result ) {
$forum_post_data = array_intersect_key(
$form_data, array_flip($this->getFieldList())
);
$result = $this->insert($forum_post_data);
}
if ( $result ) {
$this->_db_driver->commit();
}
else {
$this->_db_driver->rollBack();
}
return $result;
}
// ...
}
Note: as a good MVC practice those models should be the only place to directly operate on the table rows. Otherwise you'll end up getting SQL errors (but the data model will remain coherent, so you don't have to worry that something will break).
Finally take advantage of your models in the controller layer:
class ForumPostController extends AController
{
public function createInitialTopicPostAction()
{
$form_data = $this->getRequest()->getPost(); /* wrapper for getting
the $_POST array */
// (...) validate and filter $form_data here
$forumPost = new ForumPostModel();
$result = $forumPost->insertInitialTopicPost($form_data);
if ( $result ) {
// display success message
}
else {
// display failure message
}
}
}
The way I understand it: topics are containers of posts.
Topics table would be rather minimal, and would perhaps only contain a topic id (PK) and topic title.
The posts themselves will contain post id (PK), topic id (FK), timestamps, author id, text.
I would utilize InnoDB and foreign keys, so a topic that is deleted could delete all of its child posts.
(edit:)
In this answer I posted a way to do it using mysql_insert_id(), which would be still a technically correct solution (correct me if wrong).
However instead I will now go for the PDO wrapper I guess. And also, this is not an answer to the general modeling/approach question.
Still, following would be a way to do it:
$sql = "INSERT INTO topic VALUES (NULL,'$forumId',<more parameters>)";
$result = mysql_query($sql);
# get the generated id
$topicId = mysql_insert_id();
# and insert into the post table
$sql = "INSERT INTO post VALUES (NULL,'$topicId',<more parameters>)";
$result = mysql_query($sql);
mysql_free_result($result);
Source: http://www.desilva.biz/mysql/insertid.html
I am building an user class that manage the creation, deletion and modification of a generic user. My class should be used in this way:
# creation
user::create($username, $password, $email); // Does not need of $id
# modification
$u = new user($id);
$u->edit('password', $new_password);
# deletion
$u->delete();
Basically the class contain a static method create() that obliviously does not require the used id as argument. After the creation you can gather user infos and manage the user creating an instance of the class user and set as argument the $id of the user.
Is that a good design or should i create something like:
# creation
$users = new genericUserMethod();
$users->create($username, $password, $email);
# modification
$u = new specificUser($id);
$u->edit('password', $new_password);
# deletion
$u->delete();
...Creating 2 different classes. Or is there any other way?
two popular ways to handle this are Active Record and Data mapper. Doctrine 1 used Active record pattern and Doctrine 2 uses Data Mapper. In short:
- with active record you have class that handles both data and persistence
- with Data Mapper you have data class and class that handles persistence
Also there is Data Access Object pattern which can go on top of either of mentioned above.
Your first example looks like active record pattern with unreasonable static shorthand for building record object (why not have multiple constructors or optional id - null for new, integer for existing).
Second example looks like DAO on top of active record and looks more usual.
This could be an approach:
class User {
private $id;
private $name;
//more fields here
public function __construct($id = null) {
$this->id = $id;
if(!is_null($this->id)) {
$this->load_user_data();
}
}
protected function load_user_data() {
//select from DB where id = $this->id and populate fields
}
public function save() {
//if $this->id is null insert the user details in DB and populate $this->id with new user's id
//else update DB with field (optionally check what has changed and update only if necessary)
}
public function delete() {
//delete user if $this->id is not null
}
//fields getters and setters here as needed
}
Usage sample:
$mary = new User(); //fresh new user
echo $mary->getId(); //returns null as this user is not inserted.
$mary->setName('mary');
$mary->save(); //insert user with name mary in the DB
echo $mary->getId(); // returns an id as this user is now inserted
$john = new User(2); // we assume there was a user john in DB with id = 2
echo $john->getName(); //echoes 'john' if this was his name in DB
You can even define static methods in the class like getActiveUsers() that returns an array with the active users for example...
Note: This is intended for quite simple needs, in case you require to do dome complex things I would recommend you to use an ORM library as pointed #What is the question
The first one. Maybe you should look at ActiveRecord/ActiveModel for some further inspirations.
How can I do this in OO PHP:
A form ('in newstudent.php') asks the user to enter his name, course and year.
After selecting 'Submit' button, the page will go to 'records.php'
records.php - contains a table that displays all the records (columns: name, course, year)
when the user selects 'Submit', the new record will be added to the database which has a table named STUDENTS
SQL code
CREATE TABLE STUDENTS(
NAME VARCHAR(25) NOT NULL,
COURSE VARCHAR(25) NOT NULL,
YEAR INT NOT NULL,
CONSTRAINT STUDENTS_PK PRIMARY KEY(NAME));
*please don't mind about the primary key coz i know it's not accurate to use name as the primary key. this is just for exmple purposes.
and also...How can i manipulate data in DB using OO PHP?
Thanks
Read a book
Search Google
Create Student Object
Create Database Object
Query Database Object to insert Student Object
Well, if you want to switch to a OO method of representing students in a database, how about a 'Student' class that looks something like the definition below (although this is very basic, and not a full ORM in any way). It takes you halfway to an ActiveRecord style approach.
Note that I have assumed you will use an integer id column, not doing so makes the whole class annoying.
class Student {
var $id = -1;
var $name;
var $course;
var $year;
public static function newFromID ($id)
{
//fetch a row ($row) from the students table matching the given id
//perhaps returning false if the student doesn't exist?
return self::newFromRow($row);
}
// this method should return a new student object given a specific db row
// and should be called from newFromID. This function means that if the table
// changes, modifications only have to be made in one place
public static function newFromRow($row)
{
$obj = new Student();
//fill in the fields of the object based on the content of the row
return $obj;
}
public static function getAllStudents()
{
//perhaps return an array of student objects, by doing a broad select,
//and passing each row to newFromRow?
}
//this should save the object to the database, either inserting or updating as appropriate
public function save()
{
if($this->id == -1)
{
//insert, store the auto_increment id in $this->id
} else {
//update
}
}
}
So, to create a new student, and save it to the database:
$student = new Student();
$student->name = "John Smith";
$student->course = "French";
$student->year = 2;
$student->save();
In reality, it is often more sensible to use an existing ORM system, but if that isn't an option, you can consider writing your own.
Maybe you talk about ORM - Object Relation Mapping patterns? There are many different approaches to get mapped SQL data objects to PHP classes: Propel, Doctrine (both can be used with Symfony framework), ActiveRecord.
Of course, you can try to implement your own ORM system. You need to write data access layer for this ORM, classes which describes SQL tables and many other things. It is very interesting (for educational purposes).