Implemented integration with test_db / employee table. #1

Merged
merged 1 commit into from Feb 2, 2016
View
@@ -15,5 +15,4 @@ VERSION
/index.php
.idea
.composer
-addons/
/db/*
@@ -5,4 +5,6 @@
'sqlite:../db/sqlite.sqlite3',
//'username',
//'password'
-);
+);
+
+$config['dsn-employees'] = 'mysql://root:root@127.0.0.1/employees';
View
@@ -14,6 +14,17 @@ function init() {
$this->template->set('css','compact.css');
+ list($prefix) = explode('_',$this->page);
+ if($prefix == 'employees'){
+ $this->initEmployees();
+ }else{
+ $this->initBasic();
+ }
+
+ $this->layout->menu->js(true)->find('.active')->removeClass('atk-swatch-ink')->addClass('atk-swatch-blue');
+ }
+
+ function initBasic(){
$sm = $this->api->menu->addMenu('Core Features');
$sm ->addMenuItem('core/hello', 'Hello World');
@@ -41,10 +52,32 @@ function init() {
->link('/db')
->setAttr('title', $e->getText())
->js(true)->tooltip();
+ }
- // unable to connect.
+ }
+
+ function initEmployees(){
+ $br = $this->menu->addItem('Browse Employee Data','employees/browse');
+
+ try {
+ $this->dbConnect('dsn-employees');
+ } catch(BaseException $e){
+ $this->layout->add('Button',null,'User_Menu')
+ ->set(['Set up Employees Database', 'swatch'=>'red', 'icon'=>'attention'])
+ ->link('/employees')
+ ->setAttr('title', $e->getText())
+ ->js(true)->tooltip();
}
+
}
-}
+ function initTopMenu() {
+ $m=$this->layout->add('Menu_Horizontal',['highlight_subpages'=>true,'hover_swatch'=>'blue'],'Top_Menu');
+ $m->addItem('Basic Examples','/');
+ $m->addItem('Employee DB','/employees');
+ $m->addItem('AgileToolkit','/sandbox/dashboard');
+ $m->addItem('Documentation','http://book.agiletoolkit.org/');
+ $m->js(true)->find('.active')->removeClass('atk-swatch-ink')->addClass('atk-swatch-blue');
+ }
+}
@@ -0,0 +1,111 @@
+<?php
+class page_employees_browse extends Page {
+ public $title='Browse Employee Data';
+ function page_index(){
+
+ $this->add('View_Info')->set('A few demos to show you how to access your data on record-by-record basis. If you want to look into the code, see "admin/page/employees/browse.php" file.');
+
+
+ $t = $this->add('Tabs');
+
+ $t_dep = $t->addTab('Departments');
+ $t_dep->add('CRUD')->setModel('emp/Model_Department');
+
+ $t->addTabURL('./employees','Employees');
+ $t->addTabURL('./salaries','Current Salaries');
+ }
+
+ function page_employees(){
+ $cr = $this->add('CRUD');
+ $cr->setModel('emp/Model_Employee');
+ $cr->grid->addPaginator();
+ $cr->grid->addQuickSearch(['emp_no','first_name','last_name']);
+ }
+
+ /**
+ * Page will allow us to explore employee salaries at a gieven point in time.
+ * The interface will offer you to enter a custom date and will also show you
+ * query used to retrieve data.
+ */
+ function page_salaries(){
+
+ // Our page recognizes two get arguments. hiring_date=true will
+ // use condition on a salary join against a hiring date of an employee
+ // effectiely showing you their first salary at the time of hire.
+ // It's important to use stickyGET in here, because we don't
+ // want those GET arguments got be lost if user changes grid sorting.
+ if($this->app->stickyGET('hiring_date')){
+
+ // Specifying date parameter to a model will disable it's default
+ // date conditioning and allow us to build condition against
+ // defined model field
+ $m = $this->add('emp/Model_Employee_Salary',['date'=>false]);
+ $m->addCondition('salary_from_date','<=',$m->getElement('hire_date'));
+ $m->addCondition('salary_to_date','>=',$m->getElement('hire_date'));
+ }else{
+
+ // If date is specified, it will be used in a query. Otherwise
+ // null will be passed and model will default to today's date.
+ $m = $this->add('emp/Model_Employee_Salary',['date'=>$this->app->stickyGET('date')]);
+ }
+
+ // This box is used for displaying header with various information
+ // abone the grid.
+ $box = $this->add('View_Info');
+ $col = $box->add('View_Columns');
+ $info = $col->addColumn(2);
+ $info->add('H2')->set('Salaries');
+ $info->add('P')->set('Displaying query for 100 employee with their salary at a selected point in time.');
+
+ // Second column of our info header will contain a stacked form, where
+ // a user will be able to select a custom date for the report.
+ $form = $col->addColumn(2)->add('Form',null,null,['form/stacked']);
+ $form->addField('DatePicker','date')->set($_GET['date']);
+ $form->addSubmit(['Set Date','swatch'=>'blue']);
+ $b_hiring = $form->addSubmit('Hiring Date');
+
+ // When form is submitted we will use AJAX reloading to pass
+ // an extra parameter for this page.
+ $form->onSubmit(function($form)use($b_hiring){
+ if($form->isClicked($b_hiring)){
+ return $this->js()->reload(['hiring_date'=>true]);
+ }
+ return $this->js()->reload(['date'=>$form['date'],'hire_date'=>false]);
+ });
+
+ // The last column will display query that was used to produce the data.
+ // We do not know the query at this point yet, so we will set the
+ // contents of this view later.
+ $v_query = $col->addColumn(8);
+ $v_query->setStyle('overflow-x','auto');
+
+ // Initialize CRUD element, but don't set it up just yet.
+ $cr = $this->add('CRUD');
+
+ // Button to use the current model (with implied conditions) to
+ // perform a count() query and display list of matched records.
+ $cr->grid->addButton('Get Record Count')->onClick(function($b)use($m){
+ return 'There are '.$m->count().' employee records matching';
+ });
+
+ // Button to use the current model (with implied conditions) to
+ // display average salary
+ $cr->grid->addButton('Get Average Salary')->onClick(function($b)use($m){
+ return 'Avearge salary is '.$m->avg('salary');
+ });
+
+
+ // Limit to 100 records only and only query for relevant fields.
+ $m->setLimit(100);
+ $cr->setModel($m,['emp_no','first_name','last_name','hire_date','salary','salary_from_date','salary_to_date']);
+
+ // Still allow to do a quick-search on some fields. We will also allow
+ // user to perform search on the salary.
+ $cr->grid->addQuickSearch(['emp_no','first_name','last_name','salary']);
+
+ // Now that evertyhing is configured, we want to get a query and
+ // display it in the header. This will not contain conditions for
+ // quick-search or ordering, since those are added dureng render() stage.
+ $v_query->add('View')->setElement('code')->setHTML($m->selectQuery()->getDebugQuery());
+ }
+}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,38 @@
+
+<div id="{$_name}" class="page_{$_page} atk-size-mega">
+ <h2>Employee Database Demos</h2>
+ <p>
+ This section of the "Sink" applictaion demonstrates how to access real-life data-base through Agile Toolkit.
+ <a href="https://github.com/datacharmer/test_db" target="blank">test_db</a> is a public project containing
+ a prety sizeable data-set for testing applications and data-bases containing about 300,000 employee records
+ with about 2.8 mil. salary records. With such a data-set we focus on demonstating how to efficiently query
+ this data.
+ </p><img src="{public}image/employees-db.png{/}"/>
+ <p class="atk-effect-danger">
+ You will need to configure your database and import the data before accessing any of the demos here.
+
+ </p>
+ <h3>1. Install Employee database on your local machine</h3>
+ <p>Follow the <a href="https://github.com/datacharmer/test_db" target="blank">link to test_db project</a> then
+ click "Download ZIP" button. Run "mysql &lt; employees_partitioned.sql" in your command-line from this new
+ folder and if you have any problems, refer to test_db documentation. This will create "employees" database
+ for you.
+
+ </p>
+ <h3>2. Configure SQL access</h3>
+ <p>
+ As per "admin/config-default.php" file your default access string is "mysql://root:root@127.0.0.1/employees".
+ If you need to use a different MySQL username or password to connect, then create file "admin/config.php"
+ with the following inside:
+
+ </p>
+ <div class="atk-box"><code>
+ <pre>&lt;?php
+
+$config['dsn-employees'] = 'mysql://user:secret@127.0.0.1/employees';
+</pre></code></div>
+ <p>
+ Refresh this page and the warning on the top-rigtht corner should now dissapear.
+
+ </p>{$Content}
+</div>
@@ -0,0 +1,37 @@
+div(id="{$_name}" class="page_{$_page}").atk-size-mega
+ h2 Employee Database Demos
+
+ p.
+ This section of the "Sink" applictaion demonstrates how to access real-life data-base through Agile Toolkit.
+ #[a(href="https://github.com/datacharmer/test_db" target="blank") test_db] is a public project containing
+ a prety sizeable data-set for testing applications and data-bases containing about 300,000 employee records
+ with about 2.8 mil. salary records. With such a data-set we focus on demonstating how to efficiently query
+ this data.
+ img(src="{public}image/employees-db.png{/}")
+ p.atk-effect-danger.
+ You will need to configure your database and import the data before accessing any of the demos here.
+
+ h3 1. Install Employee database on your local machine
+ p.
+ Follow the #[a(href="https://github.com/datacharmer/test_db" target="blank") link to test_db project] then
+ click "Download ZIP" button. Run "mysql &lt; employees_partitioned.sql" in your command-line from this new
+ folder and if you have any problems, refer to test_db documentation. This will create "employees" database
+ for you.
+
+ h3 2. Configure SQL access
+ p.
+ As per "admin/config-default.php" file your default access string is "mysql://root:root@127.0.0.1/employees".
+ If you need to use a different MySQL username or password to connect, then create file "admin/config.php"
+ with the following inside:
+
+ .atk-box
+ code
+ pre.
+ &lt;?php
+
+ $config['dsn-employees'] = 'mysql://user:secret@127.0.0.1/employees';
+
+ p.
+ Refresh this page and the warning on the top-rigtht corner should now dissapear.
+
+ {$Content}
@@ -0,0 +1,15 @@
+<?php
+namespace emp;
+
+class Model_Department extends \SQL_Model {
+ public $table='departments';
+ public $id_field='dept_no';
+
+ function init(){
+ parent::init();
+
+ $this->getElement('dept_no')->editable(true)->visible(true);
+ $this->addField('dept_name');
+ }
+
+}
@@ -0,0 +1,27 @@
+<?php
+namespace emp;
+
+
+/**
+ * This model defines our access to a model table. A typicall table would have
+ * an auto-increment ID field, however the sample data uses a custom IDs, so
+ * we will have to make those editable and viewable. If you will be adding
+ * new employee, you would need to supply emp_no also.
+ */
+class Model_Employee extends \SQL_Model {
+ public $table='employees';
+ public $id_field='emp_no';
+
+ function init(){
+ parent::init();
+
+ $this->getElement('emp_no')->editable(true)->visible(true);
+
+ $this->addField('birth_date')->type('date')->sortable(true);
+ $this->addField('first_name')->sortable(true);
+ $this->addField('last_name')->sortable(true);
+ $this->addField('gender')->enum(['M','F'])->sortable(true);
+ $this->addField('hire_date')->type('date')->sortable(true);
+ }
+
+}
@@ -0,0 +1,66 @@
+<?php
+namespace emp;
+
+/**
+ * This model can dynamically calculate salaries for our employees for a
+ * desired time.
+ *
+ * $m = $this->add('emp/Model_Employee_Salary', ['date'=>$date]);
+ *
+ * $date => null -- will use today's date
+ * $date => '1990-01-01' -- will set a custom date
+ * $date => false -- will not set date condition leaving it to you
+ */
+class Model_Employee_Salary extends Model_Employee {
+
+ protected $date = null;
+
+ function init(){
+ parent::init();
+
+ if($this->date === null){
+ $this->date = date('Y-m-d');
+ }
+
+ // Creates join with a salaries table and defines necessary fields
+ $j_salary = $this->join('salaries.emp_no');
+ $j_salary->addField('salary_from_date','from_date')->type('date');
+ $j_salary->addField('salary_to_date','to_date')->type('date');
+ $j_salary->addField('salary')->sortable(true);
+
+
+ // We leave an option to disable automatic inclusion of this condition
+ // in case we want to manually define conditions
+ if($this->date)$this->addDateCondition();
+ }
+
+ function addDateCondition(){
+ $this->addCondition('salary_from_date','<=',$this->date);
+ $this->addCondition('salary_to_date','>=',$this->date);
+ }
+
+
+ // This method is missing from ATK, so adding manually (copy-pasted from sum())
+ function avg($field)
+ {
+ // prepare new query
+ $q = $this->dsql()->del('fields')->del('order');
+
+ // put field in array if it's not already
+ if (!is_array($field)) {
+ $field = array($field);
+ }
+
+ // add all fields to query
+ foreach ($field as $f) {
+ if (!is_object($f)) {
+ $f = $this->getElement($f);
+ }
+ $q->field($q->expr('avg([0])',[$f]), $f->short_name);
+ }
+
+ // return query
+ return $q;
+ }
+
+}