Codeigniter Datamapper save() not actually saving to database - php

I'm running CodeIgniter 1.7.3, Datamapper DMZ 1.7.1 (and no, upgrading is not an option), MySQL 5.1, and the Cron Job Boostrapper for an application I'm building. I'm attempting to extract information from an XML file and save it into a database. Getting the data works just fine, as does getting data from the database, so I know it's neither an issue with the database connection, nor the XML file.
$this->site = $this->site->get_by_short_name('site');
//Load the XML files into variables so we can do stuff with them.
$doctors = simplexml_load_file(realpath('../xml/doctors.xml'));
foreach ($doctors->children() as $doctor) {
$dr = new Doctor();
$attrs = $doctor->attributes();
/* See if the Doctor already exists. If so, update it.
* Datamapper won't update fields that don't change, so if nothing's
* changed, then the DB call won't be made.
*/
$dr->get_by_drkey($attrs['Key']);
$dr->drkey = $attrs['Key'];
$dr->first_name = $doctor->FirstName;
$dr->middle_name = $doctor->MiddleName;
$dr->last_name = $doctor->LastName;
$dr->long_name = $doctor->LongName;
$dr->degrees = $doctor->Degrees;
$dr->pic = $doctor->ImageURL;
$dr->save($this->site);
}
I've checked the return value of $dr->save($this->site) and it's coming back as true ("successful save"), but the data isn't saving, and there's no error. I've tried without the relation (removed the requirement for it in the database), so it should save without error, but it still doesn't save. I also double-checked the relationship setup in the models and they're fine.
I also tried bypassing Datamapper and doing a straight $this->db->insert('doctors',$data) with the data converted to an array, but the data still doesn't save. I've also tried running it from the browser (bypassing the bootstrapper) to see if the issue had to do with running from the command line, but that also didn't work. I'm also not autoloading any session or authentication libraries, so that known issue with the bootstrapper isn't being triggered.
Does anyone have any insight into why this might not be working?

I've found what was going on. It turns out that the attributes of the SimpleXML objects (the individual records) weren't getting picked up by Datamapper as strings, so they weren't getting escaped, and MySQL was choking. Typecasting them when setting the model object's values ($dr->first_name = (string)$doctor->First_Name;) fixed this problem.
I was able to get the insert query through $dr->check_last_query() after the object attempts to save.

Related

CakePHP shell: Switch CAKE_ENV on the Go

I have a little problem in here. I'm new at cakephp and now have to developing a cakephp shell script to saving data into its database. The problem is, I work on default environment and need to save data into another environment. I'm using this code to switch environment:
ConnectionManager::alias($env, 'default');
It seems good since I've got right output when try to get database.
$this->out($datasource->config()['database']);
And then I load my model:
$model = $this->Model;
But It's load the model data from default environment. Is my approach wrong? Or, there is another method to switch environment on the go with cakephp?
That should work just fine, and a quick test shows that it does. You'd probably have to show a little more context, but I guess you are loading the model (what you're showing there isn't loading but accessing) before the connection alias is being created, hence the model will use the original connection that it received when it has been instantiated.
So either make sure that you load the model afterwards, respectively that you create the alias before the model is being loaded (that is when TableRegistry::get() is being invoked), or change the connection of the specific model on the fly in case applicable:
$connection = ConnectionManager::get($env);
$model->setConnection($connection); // use connection($connection) in CakePHP < 3.4

FOSElasticaBundle: Is it possible to change "query_builder_method" in controller?

According to FOSElasticaBundle documentation it is possible to configure application to use custom query builder method like this:
user:
persistence:
elastica_to_model_transformer:
query_builder_method: createSearchQueryBuilder
But is it possible to choose QB method live, e.g. in controller action?
I'd like to be able to control what's being fetched from DB while transforming Elastica results to Doctrine entities. E.g. sometimes I'll want to do eager fetch on some relations, but can't do that by default.
Since FOSElasticaBundle documentation is not very precise, I went through its code and found it impossible to control what query builder is used on controller level.
It is possible to change whole elastica_to_model_transformer to a custom service, but still it's statically defined in configuration. Maybe with some dirty solution it would be possible going this way, but I don't think it's worth it.
I decided to just not using this feature of FOSElasticaBundle. The main problem I had was that when you use fos_elastica.index instead of fos_elastica.finder or elastica repository (in order to get plain not transformed results Elastica\Resultset), there's no findPaginated method with returns Pagerfanta paginator object, which is very helpful in my case.
Fortunately although it's not mentioned in documentation it's possible to create the Pagerfanta this way too, but a little bit more manually.
Here's a code snippet:
//generate ElaticaQuery somehow.
$browseQuery = $browseData->getBrowseQuery();
$search = $this->container->get('fos_elastica.index.indexName.typName');
//create pagerfanta's adapter manually
$adapter = new \Pagerfanta\Adapter\ElasticaAdapterElasticaAdapter($search, $browseQuery);
// now you can create the paginator too.
$pager = new Pagerfanta($adapter);
//do some paging work on it...
$pager->setMaxPerPage($browseData->getPerPage());
try {
$pager->setCurrentPage($browseData->getPage());
} catch(OutOfRangeCurrentPageException $e) {
$pager->setCurrentPage(1);
}
//and get current page results.
/** #var Result[] $elasticaResults */
$elasticaResults = $pager->getCurrentPageResults();
// we have to grab ids manyally, but it's done the same way inside FOSElasticaBundle with previous approach
$ids = array();
foreach($elasticaResults as $elasticaResult) {
$ids[] = $elasticaResult->getId();
}
//use regular Doctrine's repository to fetch Entities any way you want.
$entities = $this->getDoctrine()->getRepository(MyEntity::class)->findByIdentifiers($ids);
This actually has a few advantages. In general it gives you back control over your data and doesn't tie ElasticSearch with Doctrine. Therefore you can resign on fetching data from Doctrine if you have all needed data in ElasticSearch (if they are read only data of course). This lets you optimize your application performance but reducing amount of SQL queries.
The code above may be wrapped with some kind of service in order to prevent making mess in controllers.

Non-object error when inadvertently loading view from app/storage/views/?

I am working on a Laravel 4 application that is configured to use the file $_SESSION driver, which appears to be causing some problems for me. The problem arises randomly, and does not happen very often, and tends to happen when I try and click on an <a href="..."> quickly before the entire page is done loading (there is a large amount of information loaded on each page so page loads can be a bit slow). The error will also appear at times after waiting for the page to load completely.
Here is the error message I'm getting:
From what I understand, it looks like whenever my application decides it wants to load a (cached?) view from app/storage/views, it is not is not grabbing one or more of the objects that is passed to the view from my controller, and that is why I am getting a non-object error message.
I have tried experimenting by deleting the specific storage view in both the controller before the view is loaded and in the actual view itself (in app/views) with the following:
<?php
$badFile1 = storage_path() . '/views/d84ddef7152ff1956fc7aa87ddf29ba0';
if(file_exists($badFile1))
{
$delete = unlink($badFile1);
}
?>
But even this will still throw the error (although without showing the line #) from the file in app/storage/views, even though the file doesn't even exist:
Can anyone explain why this is happening and/or what I can do to fix this problem?
i thing you $catalog object is null. so not called getID() method.
Please check, $catalog object is assigned or not.
I've managed to fix the problem, as in, I have not been able to reproduce this random error message anymore.
It seems the error was caused by getting my models code tangled up, and not by $catalogs being null. I had two models:
Suggestions.php which does some data manipulation and Catalogs.php which ultimately fetches the $catalogs object which I needed for my view.
I was grabbing $catalogs like this in my controller:
$s = new Suggestions();
$catalogs = $s->getSuggestions($catalogID);
and then returning $catalogs in my view. The problem arises from the fact that I was also calling the Catalogs.php model (which contains the getID() function) from within the Suggestions.php model, and was not even instantiating a new instance of Catalogs to do so, just calling it straight away.
So most of the time Laravel would return $catalogs as an object of the Catalogs model like I wanted it to, but it would sometimes return it as an object of the Suggestions model, and when my view tried to call the getID() method, it was trying to call a method that did not exist in Suggestions model.
I fixed this by refactoring the Suggestions model into a private function as opposed to the model, that way $catalogs is always returning as the Catalogs object type.

Doctrine returning blank for new field added to database

I have recently added a new field to my database and have added some new getters and setters to my Doctrine model like so:
/**
* #Column(type="text", length=65000, nullable=true)
*/
private $iframeCode;
...
public function getIframeCode(){
return $this->iframeCode;
}
...
public function setIframeCode($val){
$this->iframeCode = $val;
}
This works well locally, and everything is fine. As soon as I upload to the server though, empty strings are always returned whenever I call getIframeCode() on an object, even when I have manually entered values for the field into the database. There are no errors, just empty values returned.
I thought it must be caching (I'm using APC), but I tried using apc_clear_cache() without any luck. Is there something that I'm overlooking?
Thanks.
EDIT:
I have figured out that it IS in fact the caching thats causing this. My question now would be, whats the best and safest way of clearing the cache when you're making a change like this on a production server?

CodeIgniter cache clearing

I ran into a weird problem with Codeigniter Database Cache. I'm using it to cache one query result. For this I am enabling it at the begining of the function and disabling right after:
function checkAfter($lastCheck)
{
$this->db->cache_on();
$this->db->where('time >' , date('Y-m-d H:i:s', $lastCheck));
$q = $this->db->get('actions');
$r = $q->result();
$this->db->cache_off();
return (!empty($r)) ? $r : false;
}
I need to delete the cache on the certain event when something is added to database.
It is working fine with $this->db->cache_delete_all(); but when I'm trying to remove one specific method cache like it is explained in user guide it doesn't do anything.
What might be the problem here?
Both methods are called in the same Model. I also tried to enable cache before delete but still nothing.
EDIT
Lets say i have method checkEvents in my main controller which calls the checkAfter method of the model which then works with cache. CI cache engine creates a folder called main+checkEvents under cached forlder where chached data is stored.
so i am trying to remove it with
$this->db->cache_delete('main', 'checkEvents');
call in logEvent method (which saves the new event) of that model.

Categories