Friday, July 25, 2014

Zend Lucene in Symfony Projects

In this article we will introduce you with an easy way to implement the Zend Lucene Full Text Search project in Symfony 1.4 Doctrine ORM website. We will present things a little differently then the original documentation of Symfony. We will try to explain the principle of implementation of a search engine and not providing you with code to use on set. There are things that are very specific and individual to each project and it is necessary to understand the principle according to the situation. Once you have learned how to insert this functionality in the application you can put the elements like autocompleter, advanced search and etc. They are not the subject of this article. Originally you need to download Zend Framework in the following directory:

installing Zend Lucene on symfony website
When you are testing on the localhost, it is not a problem to leave all the files on Zend Framework, but when you are uploading to the server you can leave only the following files and folders from Zend:

• Exception.php
• Loader/
• Autoloader.php
• Search/

//exams/config/ProjectConfiguration.class.php
Now we have to add these lines of code at ProjectConfiguration.class.php
Zend Lucene Full Text Search in Symfony websites
  1. static protected $zendLoaded = false;
  2. static public function registerZend() {
  3. if (self::$zendLoaded) {
  4. return;
  5. }
  6. set_include_path(sfConfig::get('sf_lib_dir').'/vendor'.PATH_SEPARATOR.get_include_ path());
  7. require_once sfConfig::get('sf_lib_dir').'/vendor/Zend/Loader/Autoloader.php';
  8.  
  9. Zend_Loader_Autoloader::getInstance();
  10. self::$zendLoaded = true;
  11. Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());
  12.  
  13. ini_set("iconv.internal_encoding", 'UTF-8');
  14. Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('utf-8');
  15. }
If you read the lesson for Zend Lucene integration in the official documentation of Symfony Framework fron here, you will notice that this code has some differences than the present example there. The differences are that we add some additional lines of code and the search works with Cyrillic letters. The next step is to choose a table from the database, which will crawl. In the example, the table is a test. In the application it is represented by two classes. It is necessary to orientate yourself to some of the things we show you because your projects’ name of the classes will be different. But once you learn the technique, it will not be a problem for you.
Programming Zend Lucene in Symfony Projects
In testTable.class.php we enter the following function:
  1. static public function getLuceneIndex() {
  2. ProjectConfiguration::registerZend();
  3. if (file_exists($index = self::getLuceneIndexFile())) {
  4. return Zend_Search_Lucene::open($index);
  5. } else {
  6. return Zend_Search_Lucene::create($index);
  7. }
  8. }
  9. static public function getLuceneIndexFile() {
  10. return sfConfig::get('sf_data_dir').'/exams.'.sfConfig::get('sf_environment').'.index';
  11. }
With this function we create folder, that looks like this:
Developing Zend Lucene in Symfony Website Projects
In this folder we keep the indexes of your entered data that are called to check on demand. Naturally, in this last row:
  1. return sfConfig::get('sf_data_dir').'/exams.'.sfConfig::get('sf_environment').'.index';
You can change the folder name to whatever you want. Now it is necessary to do two more things:
The first is to change the function save() in test.class.php
  1. public function save(Doctrine_Connection $conn = null) {
  2.  
  3. // ...
  4.  
  5. $ret = parent::save($conn);
  6. $this->updateLuceneIndex();
  7. return $ret;
  8. }
and then create the following two functions in the same file:
  1. public function delete(Doctrine_Connection $conn = null) {
  2. $index = testsTable::getLuceneIndex();
  3.  
  4. foreach ($index->find('pk:'.$this->getId()) as $hit) {
  5. $index->delete($hit->id);
  6. }
  7. return parent::delete($conn);
  8. }
  9.  
  10. public function updateLuceneIndex() {
  11. $index = testsTable::getLuceneIndex();
  12.  
  13. // Премахване на съществуващият запис
  14. foreach ($index->find('pk:'.$this->getId()) as $hit) {
  15. $index->delete($hit->id);
  16. }
  17. $doc = new Zend_Search_Lucene_Document();
  18.  
  19. // Съхраняваме първичният ключ на елемента за проверка при търсене
  20. $doc->addField(Zend_Search_Lucene_Field::Keyword('pk', $this->getId()));
  21.  
  22. // Индексираме поле
  23. $doc->addField(Zend_Search_Lucene_Field::UnStored('name', $this->getName(), 'utf-8'));
  24. //$doc->addField(Zend_Search_Lucene_Field::UnStored('description', $this->getDescription(), 'utf-8'));
  25.  
  26. $index->addDocument($doc);
  27. $index->commit();
  28. }
In the first function has there is nothing to change. In the second, the fields:
  1. $doc->addField(Zend_Search_Lucene_Field::UnStored('name', $this->getName(), 'utf-8'));
  2. //$doc->addField(Zend_Search_Lucene_Field::UnStored('description', $this->getDescription(), 'utf-8'));
appear to have the value index and search displayed.
This was our the first part of implementing to our application. Now in CMD write:
Symfony Website and Zend Lucene - web development
And now comes to creation of the route.
Symfony Website and powerfull search engine
In routing.yml we create the following path:
  1. search:
  2. url: /search
  3. param: { module: tests, action: search }
In this case, the module which will be our search engine is the tests module. Item is search.
The controller should look like this:
  1. public function executeSearch(sfWebRequest $request) {
  2. //проверяваме дали имаме post и дали този post ни е изпратил параметър query
  3. if ($request->isMethod('post') || $request->getParameter('query')) {
  4. $query = $request->getParameter('query');
  5. //проверяваме дали заявката за търсене е по голяма или равна на 3 символа
  6.  
  7. if (strlen($query)>=3) {
  8. //ако е >=3 символа изпращаме запитване и извличаме резултатите в променливата query
  9. $this->query = Doctrine_Core::getTable('tests')->getForLuceneQuery($query);
  10. }
  11. } else {
  12. //ако условието за post не е изпълнено тогава извеждаме текст : няма резултат
  13. $this->renderText('No results');
  14. }
  15. }
In SearchSuccess.php we have to develop simple code:
  1. if (isset($query) && count($query) != 0) {
  2. foreach($query as $tests):
  3. echo $tests--->getCategories()->getName();
  4. endforeach;
  5. } еlse {
  6. еcho no results”;
  7. }
In the if expression we check whether there is the variable $query and whether it contains result. Then with the foreach expression we show the result. In the example the field is only one, but in normal circumstances the variable $test contains more the one variable (name, category etc. …). Naturally with html & css we shape the results in a way we want.
//your_project/lib/model/doctrine/testTable.class.php
Now, we go back to the class tesTable.class.php and add the following function:
  1. public function getForLuceneQuery($query) {
  2. $hits = self::getLuceneIndex()->find($query);
  3. $pks = array();
  4. foreach ($hits as $hit) {
  5. $pks[] = $hit->pk;
  6. }
  7. if (empty($pks)) {
  8. return array();
  9. }
  10. $q = $this->createQuery('t')
  11. ->where('t.active = ?', 1)
  12. ->whereIn('t.id', $pks)
  13. ->limit(3);
  14.  
  15. return $q->execute();
  16. }
You can look at your controller and see that this function is called after we have entered a search term.
In part:
  1. $q = $this->createQuery('t')
  2. ->where('t.active = ?', 1)
  3. ->whereIn('t.id', $pks)
  4. ->limit(3);
  5. return $q->execute();
the simple query that we do, is checking whether the given element in the table is active and put limit 3. Thus in the match, the search will give only 3 results. Of course you can create custom queries that
meet the requirements and needs of your application.
And the last part is the form. To have permanently visible form, we will put the code in the layout.php file.
Programming Zend Lucene in web projects
In the form controller we point that after the “submit”, request is sent to the search controller. There they pass through function getForLuceneQuery($query) and if the result of the in query is displayed through the template searchSuccess.php.
We hope this article is useful for developing your websites. Even if you are not successful the first time, do not be disappointed. Nothing is too easy and for everything takes hard work and experience. Our advice is to check the way of implementation of Zend Lucene in the official web site of Symfony Framework at the jobeet project and learn our article too. The most important thing is do not stop testing.

Thursday, June 12, 2014

Steps on How to Write a New Module in Orange HRM

It is not easy to write a new module in Orange HRM since it is divided in many layers. I will guide you step by step on how to create new module in Orange HRM.

Step 1

First download latest version of Orange HRM. Then extract it, and place it to root directory. Now install the Orange HRM. After the installation is complete, your file folder should look like.

Step 2

Create a folder named orangehrmTestPlugin in plugins folder. Test is name of the plugin, you can change its name. This folder contains the code of the module.

Step 3

Now you have to enable the plugin. Go to Symfony/apps/orangehrm/config/setting.yml. Symfony folder contains the code of OrangeHRM.

Step 4

Now add plugin to admin configure list. This allows admin to easily enable or disable plugin at any time. To do this you need to include your plugin in the database. Open up ‘ohrm_module’ and ‘hs_hr_module’ tables in database and make these changes.

Status ‘1’ in ‘ohrm_module’ shows that this module is enabled.
Now the plugin needs to be included in the database. To add module in code go to ‘symfony\plugins\orangehrmAdminPlugin\lib\form\ModuleForm.php’ and make changes in there.

The admin should make changes in the check box class. Simply add these lines in ‘symfony\plugins\orangehrmAdminPlugin\modules\admin\templates\viewModulesSuccess.php’.

Now delete cache and refresh your browser. Click on Admin ->Configuration ->Modules. Your plugin will be shown there.

Step 5


Create these folders inside your plugin. Lib folder contains dao, form, and service.
Dao (Database Access Object) contains all the database operation (fetch, update, insert and delete).
Form contains forms to get data from user.
Service allows user to call dao operations.
Modules contain your plugin with 2 folders inside (actions and templates).
Actions consist of different classes that will perform actions when user interacts with system.
Default action must have a template file. An action class may have 1 or more template file.
Action file ends with Action.class.php. Templates consist of HTML code for GUI.

Step 6

Now include your plugin on main menu. To do this you need to create screen in your database. Open up ‘`ohrm_screen’ Table and insert line no 105 as shown in figure below.

ViewTestMessage is the file inside actions and templates folder. This is the default class. Whenever user clicks on Test Message execute function inside ViewTestMessageAction.class.php executes. Inside Actions folder, template against this file is viewTestMessageSuccess.php which is executed. You can create 1 or more screens in a plugin, but every screen name must be different from other.
Every screen must have a user which means that specific user can access this screen. Simply add screen id (105) and any user roleid (user role id from ‘ohrm_user_role’ Table) in‘ohrm_user_role_screen’ Table. Also specify what operation can perform this user role. Now time to show TestMessage in main Menu.
Open up ‘ohrm_menu_item’ Table. Insert menu_title as Test Message, screen_id as 105 (created in ‘ohrm_screen’ Table), parent_id as Null (if it is parent itself), level as 1 (2 for sub menu and 3 for 3rd level), order_hint as 800 (100 for most left side, 200 for second place from left, 300 for 3rd place and so on), url_extras as Null, and states as 1 (0 mean don’t show).
Now logout delete cache and login back. You will see Test Message in your main menu bar.