I have two tables like this:
CREATE TABLE `tblFacilityHrs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` varchar(45) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`description` text,
PRIMARY KEY (`id`),
KEY `key_uid` (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1$$
CREATE TABLE `tblFacilityHrsDateTimes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`owner_uid` varchar(45) DEFAULT NULL,
`startDate` date DEFAULT NULL,
`endDate` date DEFAULT NULL,
`startTime` time DEFAULT NULL,
`endTime` time DEFAULT NULL,
`days` int(2),
`recurrence` int(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_fh_owneruid` (`owner_uid`),
CONSTRAINT `fk_fh_owneruid` FOREIGN KEY (`owner_uid`) REFERENCES `tblFacilityHrs` (`uid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=52 DEFAULT CHARSET=latin1$$
id uid title description location
8ada0ceabd40d509c3fb38f2822a97de11bc6628 Swim Lessons Parent and Child
Classes CRC 2 543a6ed0005ff6a0a7fc99cc2f9715d86804ecb0 Swim Lessons
Level 1, Session 1 3 7d219b64be6dc706135bdad3e7c2f0d56cb7f353 Swim
Lessons Level 2 4 f7c91e2f1daa9c696c22f5aa5736c167d1ba9f94 Swim
Lessons Level 3 5 262f06fb75645248162aa983f610ec7959a2011b Swim
Lessons Level 4 6 51f9f552ffc5fa4bc8b4e7f914fb22b3b0920c2e Bike 275
Participate in this program and take 4 Fitness Cycling classes and get
a FREEWaterbottle! Participants can only register 1 time. This is a
FREE program! Sign up in the Multipurpose Room. 7
0cca3515ec8ee990c863e474fee634ae94d382c2 Passport to Fitness Take
Norse Fitness Classes, Get your Passport Stamped and win aFree
T-shirt! Take 8 Fitness classes between April 10 and May 1 and get a
Free T-shirt.
id owner_uid startDate endDate startTime endTime days recurrance
8ada0ceabd40d509c3fb38f2822a97de11bc6628 4/13/2012 4/13/2012 0:00:00
NULL NULL None 2 543a6ed0005ff6a0a7fc99cc2f9715d86804ecb0 NULL NULL
12:30:00 2:00:00 2 3 7d219b64be6dc706135bdad3e7c2f0d56cb7f353 NULL
NULL NULL NULL NULL NULL 4 f7c91e2f1daa9c696c22f5aa5736c167d1ba9f94
NULL NULL NULL NULL NULL NULL 5
262f06fb75645248162aa983f610ec7959a2011b NULL NULL NULL NULL NULL NULL
6 51f9f552ffc5fa4bc8b4e7f914fb22b3b0920c2e NULL NULL NULL NULL NULL
NULL 7 0cca3515ec8ee990c863e474fee634ae94d382c2 NULL NULL NULL NULL
NULL NULL
In my controllers folder I have a file named main.php with the following code:
...
function fitnessSchedule()
{
$this->config->set_item('url_suffix', '');
$crud = new grocery_CRUD();
$crud->set_model('schedule_model');
$crud->set_table('tblFitnessClasses');
$crud->join_table('tblFitnessClasses','tblFitnessClassDateTimes');
$crud->columns('title','description','location','startDate','endDate','startTime', 'endTime', 'days', 'recurrance');
$crud->display_as('title','Event')
->display_as('description','Description')
->display_as('location','Location')
->display_as('startDate','Start Date')
->display_as('endDate','End Date')
->display_as('startTime','Start Time')
->display_as('endTime','End Time');
$crud->required_fields('title','location');
$crud->set_subject('Event');
$output = $crud->render();
$this->_example_output($output);
}
function _example_output($output = null)
{
$this->load->view('main_view', $output);
}
...
In my models folder I have this:
<?php
class schedule_model extends grocery_CRUD_Model
{
function join_table($table1, $table2)
{
if($this->$table1 === null)
return false;
$select = "{$this->$table1}.*";
$select .=",$table2.startDate, $table2.endDate, $table2.startTime, $table2.endTime, $table2.days, $table2.recurrence";
if(!empty($this->relation))
foreach($this->relation as $relation)
{
list($field_name , $related_table , $related_field_title) = $relation;
$unique_join_name = $this->_unique_join_name($field_name);
$unique_field_name = $this->_unique_field_name($field_name);
if(strstr($related_field_title,'{'))
$select .= ", CONCAT('".str_replace(array('{','}'),array("',COALESCE({$unique_join_name}.",", ''),'"),str_replace("'","\\'",$related_field_title))."') as $unique_field_name";
else
$select .= ", $unique_join_name.$related_field_title as $unique_field_name";
if($this->field_exists($related_field_title))
$select .= ", {$this->$table1}.$related_field_title as '{$this->$table1}.$related_field_title'";
}
$this->db->select($select, false);
$this->db->join('uid', '$table2.owner_uid = $table1.uid');
$results = $this->db->get($this->$table1)->result();
return $results;
}
/* function join_table($table1, $table2)
{
$this->db->select('$table1.*');
$this->db->join('$table2','$table1.uid = $table2.owner_uid','left');
$this->db->get('$table1');
}*/
}
?>
I'm getting this error:
Fatal error: Call to undefined method grocery_CRUD::join_table() in
C:\xampp\htdocs\codeigniter\application\controllers\main.php on line
234
I'm basically trying to concatenate tblFacilityHrs with tblFacilityHrsDateTimes by way of UID (uid to owner_uid). I want to display both tables at once so that when a user edits the table not only do they edit the event's name/location they also edit its time/date etc.
Reference: http://www.grocerycrud.com/documentation/options_functions/set_model
I had the same question and following is the solution that I did.
Create a view with mySQL join statements for the tables that I want to join
Look for the state ($cur_state=$crud->getState();) and see if it is either "list,ajaxlist,read or success" and then I set the $crud->set_table('VIEW_NAME');
and also set the primary key $crud->set_primary_key('KEY','VIEW_NAME');
for the else part I use the table name itself so that it will help add,edit operation.
this solution working very nicely.
$crud = new grocery_CRUD();
$cur_state=$crud->getState();
$crud->set_subject('Patient Notes');
/* Use the mySQL view to display the data with related tables */
if(($cur_state=="list") || ($cur_state=="ajaxlist") || ($cur_state=="read") || ($cur_state=="success"))
{
$crud->set_table('patientsnotes_vw');
$crud->columns('noteID','Clinic_No','note','noteCreated','username');
$crud->set_primary_key('noteID','patientsnotes_vw');
if($cur_state=="read")
{
$crud->unset_fields('noteCreatedBy','user_id','patientID');
}
}
else
{
/* Use the patient_note table itself for add/edit operation */
$crud->set_table('patient_notes');
$crud->columns('noteID','Clinic_No','note','noteCreated','username');
$crud->required_fields('note');
$crud->field_type('noteCreatedBy', 'hidden', $this->userID);
$crud->field_type('patientID', 'hidden', 1);
$crud->unset_add_fields('noteCreated');
$crud->unset_edit_fields('noteCreated');
}
$crud->display_as('username','Note Created by');
$crud->display_as('noteCreated','Note Created at');
$output = $crud->render();
In this forum thread, web-johnny states that this functionality is not yet possible.
This is the 1-1 relation that grocery CRUD doesn't have this
functionality yet. It depends of how much time I will have in the
future to do that and/or if I have any good donations till then.
This functionality is a big deal and also really complicated to do it,
but I think it will save lot of time for many users when it will come
up.
Perhaps if you provide what web-johnny deems to be a good donation he might implement it.
The class grocery_CRUD ($crud = new grocery_CRUD(); )
Is localed in the folder libraries and you implement join_table in the model.
Related
I've been playing with laravel a bit and came across a weird edge case I can't quite figure out
I've got the following table structure:
CREATE TABLE `community_address` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`address_id` int(10) unsigned NOT NULL,
`community_id` int(10) unsigned NOT NULL,
`is_billing` tinyint(1) NOT NULL DEFAULT '1',
`is_service` tinyint(1) NOT NULL DEFAULT '1',
`is_mailing` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
)
CREATE TABLE `communities` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
)
CREATE TABLE `addresses` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`address_1` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Street address',
`address_2` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Street adddress 2 (Company name, Suite, etc)',
`city` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'City',
`state` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'State / Province',
`zip` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Zip / Postal Code',
`country_id` int(10) unsigned NOT NULL COMMENT 'Country ID',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
)
Which i've represented with the following Laravel Model for a community
class Community extends Model
{
public function addresses(){
return $this->belongsToMany(Address::class, 'community_address', 'community_id', 'address_id');
}
}
$community->addresses() does in fact return only addresses for the community, but say I want to filter by address type in my pivot table (billing, mailing, etc)
I can try this:
public function getBillingAddress(){
return $this->addresses()->wherePivot('is_billing','=', true)->firstOrFail()->get();
}
Which does return results, however it's EVERY row in my pivot table matching my query, not running my query off the existing addresses
So my second idea was to use the 'and' boolean argument like so
public function getBillingAddress(){
return $this->addresses()->wherePivot('community_id', '=', $this->id, true)->wherePivot('is_billing','=', true)->firstOrFail()->get();
}
Which results in the following SQL which errors out (for obvious reasons), but also doesn't quite look like it's searching for what i'd want, even if it did work?
select `addresses`.*, `community_address`.`community_id` as `pivot_community_id`, `community_address`.`address_id` as `pivot_address_id` from `addresses` inner join `community_address` on `addresses`.`id` = `community_address`.`address_id` where `community_address`.`community_id` = 2 1 `community_address`.`community_id` = 2 and `community_address`.`is_billing` = 1 limit 1
Which looks to me like the "and" value is not, in fact, a boolean value, but is printing the value as a string straight to the query.
I tried the obvious, and tried to swap the forth argument with "and" and the following sql was generated, which doesn't fail, but returns all addresses, not just addresses linked to my community
select `addresses`.*, `community_address`.`community_id` as `pivot_community_id`, `community_address`.`address_id` as `pivot_address_id` from `addresses` inner join `community_address` on `addresses`.`id` = `community_address`.`address_id` where `community_address`.`community_id` = 2 and `community_address`.`community_id` = 2 and `community_address`.`is_billing` = 1 limit 1)
Am I missing something obvious here?
With some tinkering with the result SQL I can get what I want, which is the following raw sql query:
select `addresses`.*,
`community_address`.`community_id` as `pivot_community_id`,
`community_address`.`address_id` as `pivot_address_id`
from `addresses`
inner join `community_address` on `addresses`.`id` = `community_address`.`address_id` and `community_address`.`community_id` = 2 and `community_address`.`is_billing` = 1
limit 1
How can I achieve the same SQL being generated for me via eloquent?
I think This will be userfull For you If I come up with an example
we have Users Role And Role_User Tables
we have connect Users To Role with belongs To Many And we want use select:
Users models:
function Roles()
{
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
}
in Our Controller we can write any select like bellow:
class exampleController extends Controller
{
public function index()
{
User::with(['Roles'=>function($query){$query->where(....)->get();}])->get();
}
}
you can Use any select on query and return what ever you want..
just be carefull if you need to use any varible in your select you must use
bellow format
class exampleController extends Controller
{
public function index()
{
$var =...;
User::with(['Roles'=>function($query) use ($var){$query->where(....,$var)->get();}])->get();
}
}
i hope this will solve your problem...
It seems I misunderstood how wherePivot() worked, changing the code to the following worked:
public function getBillingAddress(){
return $this->addresses()->wherePivot('is_billing', '=', true)->get()->first->all();
}
Where the new code is trying to call the is_billing column of the pivot table to further filter the existing table, the old one was trying to filter it by what it was already filtered by, but since it was an inner join, it was returning all the rows (At least I think?)
Either way, this is solved, hope this helps someone in the future.
I am following instructions from Bookmarks tutorial, and i have a problem with one of the queries.
I have baked all models from my database (like in tutorial) and now i want to prepare custom finder.
I have two tables Academic Teachers and Evaluations
CREATE TABLE evaluations (
ID int(10) NOT NULL AUTO_INCREMENT,
academic_teacher_ID int(10) NOT NULL,
framework_ID int(10) NOT NULL,
protocol_ID int(10) NOT NULL,
final_note DOUBLE,
room varchar(255),
PRIMARY KEY (ID)) ;
CREATE TABLE academic_teachers (
ID int(10) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
surname varchar(255) NOT NULL,
national_identification_number int(10) NOT NULL,
occupation varchar(4),
degree varchar(255),
department varchar(255),
PRIMARY KEY (ID)) ;
I want to find teachers, who at their last evaluation received a low note.
public function findLowNotes(Query $query, array $options)
{
return $this->find()
->distinct(['AcademicTeachers.id'])
->matching('Evaluations', function ($q) {
return $q->where(['Evaluations.final_note' < 3]);
});
But it wil find all the teachers who ever had any bad note. How should it be? Shall I somewhere use one of the ways of retrieving last ID that I found? Or there is a more clever way that will surely work? I am much confused how to combine it all together.
And - is it possible to join here with that final_note, so I could paginate it along with that teacher's data?
Yours sincerely,
Milven
This is not right.
return $q->where(['Evaluations.final_note' < 3]);
It should be.
return $q->where(['Evaluations.final_note <' => 3]);
I looked over Google for some samples/solutions about this, for example Creating Discount Code System (MySQL/php) but I haven't found a good solution.
My situation is such that I have a platform, where the user is supposed to have a balance in a virtual currency, and can buy virtual items for it. Now there's a wish to implement vouchers and discounts. There would be different kinds of codes, like one that gives 50% discount on purchasing items, x amount of extra items (with or without minimum item amount), just a code to get some currency, or a reference code that gives the referrer something.
I have implemented it as Campaign and CampaignType, where first holds the campaign info and second holds the action info.
Here's the structure:
-- Table structure for table `cake_campaigns`
CREATE TABLE IF NOT EXISTS `cake_campaigns` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8 NOT NULL,
`code` varchar(100) COLLATE utf8_bin NOT NULL,
`type_id` varchar(50) CHARACTER SET utf16 COLLATE utf16_bin NOT NULL DEFAULT '1',
`value` int(10) unsigned NOT NULL DEFAULT '5' COMMENT 'Percentage or amount',
`min_amount` bigint(20) unsigned NOT NULL DEFAULT '0',
`owner_id` bigint(20) unsigned NOT NULL,
`created` datetime NOT NULL,
`active` tinyint(1) unsigned NOT NULL DEFAULT '1',
`single_use` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`),
KEY `owner_id` (`owner_id`),
FULLTEXT KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=4 ;
-- Table structure for table `cake_campaign_types`
CREATE TABLE IF NOT EXISTS `cake_campaign_types` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8 NOT NULL,
`unit` varchar(10) CHARACTER SET utf16 NOT NULL DEFAULT '%',
`multiplier` double(10,8) NOT NULL DEFAULT '0.01000000',
`type` varchar(50) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=7 ;
Currently my logic is that when a campaign is used, then the action is according to CampaignType's name, for example in the purchase logic:
if (isset($this->request->data['Purchase']['code'])) {
$code = $this->request->data['Purchase']['code'];
$campaign = $this->Campaign->findByCode($code);
$this->Campaign->id = $campaign['Campaign']['id'];
// campaign no longer active
if ($this->Campaign->field('active') == 0) $code = false;
if ($this->CampaignLog->find('first', array('conditions' => array(
'user_id' => $this->User->field('id'),
'campaign_id' => $this->Campaign->field('id'),
'activated' => 1,
)))) $code = false; // code has already been used
unset($this->request->data['Purchase']['code']);
} else $code = false;
// Some purchasing logic here
if ($code) {
$this->CampaignLog->create();
$this->CampaignLog->save(array(
'campaign_id' => $this->Campaign->field('id'),
'user_id' => $this->User->field('id'),
'activated' => 1,
'source' => $this->Session->read('referrer'),
'earnings' => $earned,
'created' => strftime('%Y-%m-%d %H:%M:%S'),
));
if ($this->Campaign->field('single_use') == 1) {
$this->Campaign->saveField("active", 0);
}
// Apply code here
}
Now, my question is:
What would be the best course of action on going about applying those codes, because I'm a bit queasy on going with if-then-else or switch-case through all the possible code types. But right now, since there's so many things that can be different (ex. Discount - in percentage or set amount), then that seems to be the only option.
Maybe the structure/logic of the codes should be different?
It's already straightforward in my point of view, integrating it with the purchase would be the best bet in knowing further problems. Assuming that we have $this->request->data['price'] for the price, then we have an example type_id of 1 that represents as a discount.
All we have to do is to get value and do percentage equation so that would be like
$discount = floatval('0.' . $this->Campaign->value);
$finalPrice = $this->request->data['price'] * $discount;
Better if we implement it on a switch case to isolate their logic. It may depend on how you implement it but that's the gist of the concept.
Check the cart plugin it is using events for everything and nothing is hardcoded, it is pretty flexible by using this approach.
There is one method that is fired whenever the cart needs to be recalculated. Inside it calls other methods to calculate taxes and discounts.
Implementing this through events has the advantage that it is very easy to extend with additional discounts or tax calculations later.
Feel free to review the whole code of the plugin, it is not a simple implementation and covers cookie, session and database storage for the cart data and has events for a lot of things.
I have 2 tables
CREATE TABLE `tbl_patient` (
id_patient INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(25) NOT NULL DEFAULT "not available",
att1 VARCHAR(5 ) NOT NULL DEFAULT "att1",
att2 VARCHAR(25) NOT NULL DEFAULT "att2",
att3 VARCHAR(25) NOT NULL DEFAULT "att3",
CONSTRAINT `uc_Info_patient` UNIQUE (`id_patient`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
and
CREATE TABLE `tbl_patient_medicine` (
id_patient_medicine INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_patient INTEGER NOT NULL,
name_medicine VARCHAR(50) NOT NULL DEFAULT "",
dosis VARCHAR(50) NOT NULL DEFAULT "",
start_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
treatment VARCHAR(50) NOT NULL DEFAULT "",
times_per_day VARCHAR(50) NOT NULL DEFAULT "",
CONSTRAINT fk_ID_Patient_Medicine FOREIGN KEY (id_patient) REFERENCES `tbl_patient`(id_patient)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
As you can see table patient_medicine is the intermediate table of between tbla_medicines, and table patient.
Now I want to consult all data from tbl_patient_medicine with grocery crud like in this sqlfiddle
supossing I pass the id in the uri (in the example will be id_patient=1)
I have
public function details_medication($my_id = 0)
{
try{
$crud = new grocery_CRUD();
$crud->where('id_patient',$my_id);
$crud->set_table('tbl_patient_medicine');
//HOW TO DO IT?
$crud->set_relation('id_patient', 'tbl_patient', 'id_patient');
$output = $crud->render();
$this->_output($output);
}catch(Exception $e){
show_error($e->getMessage().' --- '.$e->getTraceAsString());
}
}
SO I have tried different ways but got errors like this:
A Database Error Occurred
Error Number: 1052
Column 'id_patient' in where clause is ambiguous
SELECT `tbl_patient_medicine`.*, j7a675883.id_patient AS s7a675883
FROM (`tbl_patient_medicine`)
LEFT JOIN `tbl_patient` as j7a675883
ON `j7a675883`.`id_patient` = `tbl_patient_medicine`.`id_patient` WHERE `id_patient` = '1' LIMIT 25
Line Number: 87
I did your example:
Controller:
function medicine()
{
$crud = new grocery_CRUD();
$crud->set_table('tbl_patient_medicine');
$crud->required_fields('id_patient','name_medicine','dosis','start_date','treatment','time_per_day');
$crud->columns('id_patient','name_medicine','dosis','start_date','treatment','time_per_day');
$crud->fields('id_patient','name_medicine','dosis','start_date','treatment','time_per_day');
$crud->set_relation('id_patient','tbl_patient','name');
$output = $crud->render();
$this->_example_output($output);
}
It works!
Edited:
$crud->set_relation('id_patient','tbl_patient','{name}, {att1}, {att2}, {att3}');
Try by changing WHERE j7a675883.id_patient
I'm a bit confused. I have a website with a sort of user-profiles. When a visitor hits a user-page I want to update the number of views by a date and userid. But, no matter what i do, the number of views is updated with 2 instead of one. I've created an query-output for all queries which are executed during a page-request. The update-query is correct and there's only 1 update-query executed during the page-request.
This is my data-structure:
CREATE TABLE `ProfileView` (
`Id` int(8) NOT NULL auto_increment,
`UserId` int(8) NOT NULL,
`Date` date NOT NULL,
`Views` int(8) NOT NULL,
PRIMARY KEY (`Id`),
KEY `UserId` (`UserId`,`Date`)
) ENGINE=MyISAM AUTO_INCREMENT=10 DEFAULT CHARSET=latin1;
No matter what I do, the column 'Views' is always updated by 2 instead of 1.
The logic being executed (called from a controller, controller gets called from the view. Decorator is basically a sealed stdClass providing strict coding guidance because misspelled properties result in a PropertyDoesntExistException):
Workflow:
# user-details.php
$oControllerProfileView = new Controller_ProfileView();
$oControllerProfileView->Replace($iUserId);
---
# Controller.ProfileView.php
public function Replace($iUserId) {
// validation
Model_ProfileView::Replace($iUserId, date('Y-m-d'));
}
---
# Model.ProfileView.php
static public function Replace($iUserId, $sDate) {
$oData = MySQL::SelectOne("
SELECT Views
FROM ProfileView
WHERE UserId = ".$iUserId."
AND Date = '".$sDate."'");
if(is_a($oData, 'Decorator')) {
MySQL::Query("
UPDATE ProfileView
SET `Views` = ".($oData->Views + 1)."
WHERE UserId = ".$iUserId."
AND Date = '".$sDate."'");
} else {
MySQL::Query("
INSERT INTO ProfileView
VALUES (
NULL,
".$iUserId.",
'".$sDate."',
1
)");
}
}