Change the way CakePHP associate models - json

I'm trying to split out some JSON strings in order to be parsed by this RestKit's iPhone library, but CakePHP is splitting out an incompatible string. For example, the string below is what it's currently splitting out:
1. {"Question":{"id":"1","content":"Test","player_id":"1","points":"0","votes":"0","created":"0000-00-00 00:00:00"},"Player":{"id":"1","username":"player_test"}}
I need to have something like:
2. {"Question":{"id":"1","content":"Test","player_id":"1","points":"0","votes":"0","created":"0000-00-00 00:00:00","Player":{"id":"1","username":"player_test"}}}
Note that the Player response should be part of Question.
The way the models are setup on Cake is that 'Question' belongs to 'Player' which the latter hasMany 'Question'
I am looking for the proper way of telling Cake to output something like the response #2 above. Any suggestions?

You can use afterFind() callback of your Question model to nest the Player record inside the Question record. Or modify the results array as required after fetching. The various function of Hash class might help you in reformatting the array.

You can add a custom method to your Question model that returns the result in the desired format. This will keep your code clean and keep the data-processing/formatting logic in your Model (where it should be in most cases);
For example, inside your Question model:
public function getQuestionsForPlayer($playerId)
{
$results = $this->find('all', array(
'fields' => array(/* fields */),
'conditions' => array(/* ..... */
/* etc */
));
// Process your result to be in the right format
// Hash::extract() and other Hash:: methods
// may be helpful here as #ADmad mentioned
return $processedResult;
}
As ADmad mentioned, the Hash utility may be helpful. Documentation is located here:
http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html

Related

Make doctrine return databasefields as keys for returned data

I'm retrieving data from a Mysql database, and this is done via mapping my entity properties to database fields using Doctrine with annotations as shown in this
image. Retrieving the data is not the problem, but my property name is used as key for the returned data, which is not what i want.
Example:
I have a property gridId (camelCase), which is mapped to a database field called grid_id (snake_case) as shown in the image above. The key for the rows returned for this property will be gridId, and not grid_id, which is desired in my case.
An example of the current situation:
image.
The desired situation:
$grid =
[
grid_block_id = 18
width = 50
width_pixels = 50
]
Changing either the database fields or the property names is not an option in my case.
EDIT
I'm using JMS as serializer. I guess the 4th option of #Jakumi's answer is probably the best option for my use case. Somehow use the serializer to return the property's column name as key for returned values.
My struggle when retrieving data from doctrine is that i have to manually adjust the keys fetched by doctrine as shown in this image as this is how the front-end expects it to be.
so what you're saying is ... you want to actively ignore the object relational mapping (ORM) that the doctrine ORM you're using is providing you and which actually provide the keys exactly the way your entity names them?
I assume there is some good (or not so good) reason for this. Depending on your use case, different solutions may be the best. Options including (but probably very incomplete):
option 1: add a function to the entity
class Grid {
/** all your fields **/
public function toArray() {
return [
'grid_id' => $this->gridId,
// ...
];
}
}
option 2: don't use the orm
add a function in your GridRepository like:
function getRawAll() {
$conn = $this->getEntityManager()->getConnection();
$_result = $conn->query('SELECT * FROM grid');
$results = [];
while($row = $_result->fetch()) {
$results[] = $row;
}
return $results;
}
and similar. Those are obviously circumventing the ORM. The ORM is for retrieving objects, and those have specified fields.
option 3: return the given array back to the unmapped state
use the EntityManager's getClassMetadata (ClassMetadataInfo->fieldMapping) to fetch the mapping for your fields, and "revert" the renaming.
This is probably slightly better than option 1 to some degree, since it doesn't have to be updated.
option 4: use a serializer that does your property name translation.
since you're apparently already using the jms serializer, the SerializedName annotation can probably help, although the default appears to be snake case, but maybe somewhere it's overridden? you might want to check your configuration ...

Can models disregard all entries with 'deleted' attribute = 1 in ActiveRecord?

I have a model called Document, and in this model I've overridden the delete()function to just update the attribute deleted to 1.
This model is in the middle of a web of relations. It belongs to one or more DocumentCategory, and it has one or more versions of DocumentFile, so I would like to disregard all deleted rows without having to write ->where(['deleted' => 0]) in all my relational queries.
How can I solve this fast and yii-like?
It can be done like this:
public static function find()
{
return parent::find()->where(['deleted' => 0]);
}
So when you call your Document::find()->all() for example where will be added.
But its not good idea to owerwrite find like this, better make function called findActive() with same contents and use it Document::findActive()->all().

CakePHP View with json format

I 'm going to make a CakePHP application which collaborates with a mobile application. Mobile application is going to query CakePHP app to check if some vouchers are valid or not. I need to change some of my views such that their outputs be in json format in order to be parsed by mobile application easily.
To be specific if mobile app calles example.com/vouchers/check/1234 Cake should return something like this as response: {"validity":"valid"} or {"validity":"invalid"} which is the result of checking the validity of voucher with id 1234.
Basically, you should use extensions when you expect non HTML responses (JSON in this case)
So request
/vouchers/check/1234.json
and use the JsonView as per docs and as per ajax-and-cakephp tutorial.
To sum it up:
Use this to allow json extension being enabled:
Router::parseExtensions();
Router::setExtensions(array('json', ...));
Don't forget to include the RequestHandler component in the controllers $components list.
Add this to your action:
$data = array(
'validity' => ...,
);
$this->set(compact('data')); // Pass $data to the view
$this->set('_serialize', 'data'); // Let the JsonView class know what variable to use

ZF2 Configuration Injection Or NOT?

I've googled a lot and read as much as I can on the subject but I cannot find a direct answer to my question anywhere including here. My ZF2 application consists of roughly 7 different modules. 5 of the modules will need access to the same database configuration. The application itself has a database with roughly 124 different tables. So the idea here is to find the proper solution to write the least amount of code considering the setup.
What I'm trying to do is create a specific class to interface with the DB. Where the business logic is kept in the Module and note the controllers to keep everything more abstract and easier to maintain. By that I mean controller X should be able to create a new instance of for instance (Application\Model\DBInterface) and use the models functions to do inserts deletes updates joins selects and so forth. The reason I would like to do it this way is so that all modules installed can use the same interface without having to write endless DI statements everywhere. So what I will need is an example of how I can get the configuration for the DB (currently inside module.config.php + local.php(username / pw)) to be passed to the Application\Model\DBInterface dbConfig variable, and perhaps even an instance of the dbAdapter initialized from config if possible.
Alternatively I could potentially grab the configuration from the Application\Model\DBInterface if such a way exists.
If neither of the above is possible then I can always go back to the old way of doing things by reading an ini file for the db details and instantiating my db adapter that way.
Please keep in mind that I won't be injecting anything in the controllers as the controllers just use $db = new \Application\Model\DBInterface() so injecting into the controllers doesn't make much sense at all.
Is there a better way to do this / optimized / am I doing it completely wrong? Anyone able to share some details please. I've spent way too much time on this already and definitely need help.
Okay, so #Ocramius did just let me know what my misconception with the initializers was and helped me out a bit in understanding it. So here is a probably working solution to your Problem. My understanding about your problem is:
"How to get a DbAdapter set for all your Models implementing a DbInterface". This is how you'd do it:
Step 1: Create invokables for all classes implementing the DbInterface. Create a factory for the default Zend\Db\Adapter\Adapter and then create an initializer for your DbInterface
Module.php getServiceConfig()
return array(
'invokables' => array(
'application-model-one' => 'Application\Model\One',
'application-model-two' => 'Application\Model\Two'
),
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory'
),
'initializers' => array(
'DbInterfaceInitializer' => function($instance, $sm) {
if ($instance instanceof \Application\Model\DBInterface) {
$instance->setDbAdapter($sm->get('Zend\Db\Adapter\Adapter'));
}
},
)
)
The Zend\Db\Adapter\Adapter is using the top-level-configuration-array-key 'db' to automatically inject the dbParams
Step 2: Create your classes implementing your Interface
Application\Model(One|Two|N).php
namespace Application\Model;
class One implements DbInterface, \Zend\Db\Adapter\AdapterAwareInterface
{
/**
* #var \Zend\Db\Adapter\Adapter $dbAdapter
*/
protected $dbAdapter;
public function setDbAdapter(\Zend\Db\Adapter\Adapter $dbAdapter) {
$this->dbAdapter = $dbAdapter;
}
public function getDbAdapter() {
return $this->dbAdapter;
}
// More of your business logic or data here
}
Step 3: Access those classes with the ServiceLocator from your Controllers
SomeController.php someAction()
$dbOne = $this->getServiceLocator()->get('application-model-one');
$dbTwo = $this->getServiceLocator()->get('application-model-two');
// Adapter will automatically be injected
When accessing the invokable from the ServiceManager the initializer will be called. The initializer then will automatically call the Zend\Db\Adapter\Adapter, which in turn get's the parameters from the configuration key 'db'
You may get more information from once the tutorial Application as well as the blog of
samsonasik: ServiceManager Cheat-Sheet

Extend current API's in Magento to get items in JSON format

I am a beginner to Magento. I am trying to extend the current API classes in Magento to fulfill my requirements and retrieve data in JSON format. I need:
I need to get all stores in a website
I need to get all Categories and Subcategories in a specific store
I need to get all products in a specific Category.
All data retrieved should be in JSON format.
Any blog/Forum topic? Any kind of help?
Thanks in advance.
Please refer to this Magento wiki page http://www.magentocommerce.com/wiki/doc/webservices-api/custom-api#creating_custom_adapter_for_api.
Steps:
You need to create a new API Server Adapter that should implement Mage_Api_Model_Server_Adapter_Interface.
Create a controller that will run your api server adapter
Implement Mage_Api_Model_Server_Adapter_Interface::run() method for process JSON request and return result in JSON. See Mage_Api_Model_Server_Handler_Abstract for understanding Magento API workflow.
never been is such situation , but an idea came to mind is to invoke a SOAP service or XML-RPC , then convert whatever data needed to JSON.
Magento offers SOAP or XML-RPC web service to be automatically generated with specific roles for users, very useful.
That would be a better approach and it is not at all complicated. Refer to this to see.
http://www.magentocommerce.com/wiki/5_-_modules_and_development/web_services/additional_information
See here
https://github.com/app-z/magento-android-web-api
There is even Random Products list
Is it what you want?
//
// Random Products Items
//
// http://localhost/magento/web-api.php?route=feed/web_api/random&limit=4&key=key1
//
function random_products($limit){
$json = array('success' => true);
$products = Mage::getModel('catalog/product')->getCollection();
$products->addAttributeToSelect(array('name', 'thumbnail', 'price')); //feel free to add any other attribues you need.
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($products);
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($products);
$products->getSelect()->order('RAND()')->limit($limit);
foreach($products as $product){
$json['products'][] = array(
'id' => $product->getId(),
'name' => $product->getName(),
'href' => $product->getProductUrl(),
'thumb' => (string)Mage::helper('catalog/image')->init($product, 'thumbnail'),
'pirce' => Mage::helper('core')->currency($product->getPrice(), true, false) //." ".$currencyCode,
);
}
return $json;
}
Inchoo has written a free REST, JSON, and AMF adapter for Magento. You can find it here: http://www.magentocommerce.com/magento-connect/inchoo-api.html