How to retrieve result data foreach in foreach on cakephp - cakephp-2.0

I like to produce some universal function for my project
in a harcode php mode it would be like :
<select id="segment" name="segment">
<option value=''></option>
<?php
$sql = "select segment_id, segment_name, segment_parentid
from test_segment where segment_parentid = 'root' and segment_status = '1' order by segment_name asc";
$sql = $koneksi_db->sql_query($sql);
while($row = $koneksi_db->sql_fetchrow($sql)){
printf( "<option value=$row[segment_id] disabled>".ucwords($row['segment_name'])."</option>" );
$sql1 = "select segment_id, segment_name, segment_parentid from test_segment where convert(varchar, segment_id) <> 'root' and
segment_parentid= '$row[segment_id]' and segment_status = '1' order by segment_name asc";
$sql1 = $koneksi_db->sql_query($sql1);
while($row1 = $koneksi_db->sql_fetchrow($sql1)){
printf( "<option value=$row1[segment_id]> |_".ucwords($row1['segment_name'])."</option>" );
}
}
?>
</select>
How to convert that code into cakephp MVC method code? the Controller and Model
, I shall not to breaking MVC method with hardcode way for this code.
note : $koneksi_db->sql_query, $koneksi_db->sql_fetchrow was my own function for query process, and for query I cannot change into cakephp method, because it was restrict procedure from my mentor.

One way is to create a model representing the dataset you want. Then load the model for any controller that needs access to the data and that controller's view using that data in the in the select.
Step1: Create a MySQL view to represent the two SQL statements:
CREATE VIEW segments AS
SELECT segment_id, UPPER(segment_name), segment_parentid, UPPER(segment_name) AS sortfield
FROM test_segment
WHERE segment_parentid = 'root' AND segment_status = '1'
UNION
SELECT UPPER(test_segment.segment_id), test_segment.segment_name, test_segment.segment_parentid, UPPER(CONCAT(parent.segment_name, test_segment.segment_name))
FROM test_segment
JOIN test_segment AS parent
ON test_segment.segment_parentid = parent.segment_id
AND parent.segment_status = '1'
WHERE convert(varchar, test_segment.segment_id) <> 'root'
AND test_segment.segment_status = '1';
Step2: Create Model for this
<?php
App::uses('AppModel', 'Model');
class Segment extends AppModel {
// the name of the view
public $useTable = 'segment';
public $displayField = 'segment_name';
public $primaryKey = 'segment_id';
}
Step3: In whatever controller method you want this, Use this code
$this->loadModel('Segment');
$segments = $this->Segment->find('list', array('order => array('sortfield')));
set('segments', $segments);
Step4: Then in that controller's view, add the element (I'm assuming you will use the form helper, otherwise you can write the html out:
echo $this->Form->select('segments',$segments);

Related

How to write a join query with "two database" in cakephp

How to write a join query with two database in cakephp. Here is my code and I want to connect two db connection but I have a single Db Connection.
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Datasource\ConnectionManager;
use src\Model\Table\classScheduleDbConnTable;
class TimeTableController extends AppController {
public function index() {
$jpDB = ConnectionManager::get('default');
$ClsDB = ConnectionManager::get('test');
$result = $ClsDB->execute('SELECT * FROM t_class_schedule')->fetchAll('assoc');
$data=array();
foreach ($result as $key => $value) {
$data[$key]['id'] = $value['id'];
$dates = date("Y-m-d", strtotime($value['classDate']));
$data[$key]['fromTime'] = $value['fromTime'];
$data[$key]['toTime'] = $value['toTime'];
$data[$key]['keyWord'] = $value['keyWord'];
$data[$key]['classDate'] = $dates;
}
$this->set('datas',$data);
}
}
I don't believe you can just "connect" two ConnectionManagers in this way.
You probably have two options:
Option A - Specify the database name statically as part of the Table's initialization per the example in this post:
public function initialize(array $config)
{
$this->table('databaseName.tableName');
// ...
}
Option B - Specify an alternative connection & Lazy Load
You can specify an alternative connection names for a specific Model at the Table level:
class ClassSchedule extends Table
{
public static function defaultConnectionName() {
return 'test';
}
}
Note that this can't be used in a join on other Tables using the default connection. You'll instead have to "lazy load" associated data manually. As a general example (I don't know what your associations are, just a random example):
$student->class_schedules = TableRegistry::get('ClassSchedules')->find()
->where(['student_id'=>$student-id])
->toArray();

How can I call Model::save() using a custom primary key in CakePHP 2.x?

I want to update a record:
//Update: id is set to a numerical value
$this->Recipe->id = 2;
$this->Recipe->save($this->data);
This is working fine.
But it won't work if I try to update the record based on tmp_id:
$this->Recipe->tmp_id = 2;
$this->Recipe->save($this->data);
I am aware of function Model::updateAll(). But I want to use Model::save() instead.
The following should work:
$this->Recipe->primaryKey = 'tmp_id';
$this->Recipe->id = 2;
$this->Recipe->save($this->data);
Or you could make changes permanent by defining tmp_id as the primary key in your Model:
class Recipe extends AppModel {
public $primaryKey = 'tmp_id';
}
Update statement in cakephp :
public function update_data($tmp_id){
if($this->request->is('post'){
$this->Recipe->id = $tmp_id;
$this->Recipe->save($this->data);
}
}
Here update_data is Dummy function for your understanding.

Is there a way to add created_by and modified_by similar to how created and modified works in cakePHP?

I'm wondering if there's a way to add created_by and modified_by similar to how created and modified work in CakePHP?
I like the fact that cake recognizes those fields and takes care of them automatically, regardless of the model, without me having to implement them at all. I would like to add a similar feature using the current user id (in my application, there is always a userid, even if it sometimes may be 0).
I assume the starting place is before_save() in app_model?
--
Also, is there any way for me to get cake to recognize this as a foreign key to the user table automatically (similar to how it recognizes user_id), or will I have to add the has/belongs to relationship manually? I ask because this will go on most of my models, so I'd prefer to reduce redundant code.
Thank you!
For the first part of your question, I use this Behavior code to do exactly what you are looking for:
class UserLinkBehavior extends ModelBehavior
{
/**
* The string to use to retrieve the user id from CakeSession
*
* #var string
*/
var $user_id_key = 'Auth.User.id';
function setup(&$model, $settings)
{
if(isset($settings['user_id_key']))
{
$this->user_id_key = $settings['user_id_key'];
}
}
function beforeSave(&$model)
{
App::uses('CakeSession', 'Model/Datasource');
$logged_user_id = CakeSession::read($this->user_id_key);
if(isset($logged_user_id))
{
$this->set_user_on_current_model($model, $logged_user_id);
}
return true;
}
/**
* Set the created_by and modified_by user id on the current model
*
* #param Model $model
* #param int $logged_user_id
* #return void
*/
private function set_user_on_current_model(&$model, $logged_user_id)
{
if(isset($logged_user_id))
{
/*
* Id is not set -> it is a creation
*/
if($model->hasField('created_by') && (!isset($model->data[$model->alias]['id']) || empty($model->data[$model->alias]['id'])))
{
if(!isset($model->data[$model->alias]['created_by']))
{
$model->data[$model->alias]['created_by'] = $logged_user_id;
/*
* If the save is called with a whitelist, add 'created_by' to the whitelist
* in order to have this field saved as well
*/
if(!empty($model->whitelist) && !in_array('created_by', $model->whitelist))
{
$model->whitelist[] = 'created_by';
}
}
}
/*
* Id is set -> it is an update
*/
if($model->hasField('modified_by') && isset($model->data[$model->alias]['id']) && !empty($model->data[$model->alias]['id']))
{
$model->data[$model->alias]['modified_by'] = $logged_user_id;
/*
* If the save is called with a whitelist, add 'modified_by' to the whitelist
* in order to have this field saved as well
*/
if(!empty($model->whitelist) && !in_array('modified_by', $model->whitelist))
{
$model->whitelist[] = 'modified_by';
}
}
}
}
}
Then just declare it in your Model or your AppModel
var $actsAs = array('UserLink');
For the second part of your question, you could probably add a beforeFind() callback to the behavior and use the model->bindModel() function to link the model having the created_by and modified_by fields with a User model. Personaly I prefere to declare these links in each model manually when I need them.
It cannot be like the created and modified field but you can add this fields in the controller method wherever you want.
eg. in the add method you can add like follows.
$this->request->data['ModelName']['created_by'] = $this->Auth->user['userid'];
I found this one and working good for me its easy to implement and understand
<?php
App::uses('Model', 'Model');
class AppModel extends Model {
//get current logged-in user
public function getCurrentUser() {
App::uses('CakeSession', 'Model/Datasource');
$Session = new CakeSession();
$user = $Session->read('Auth.User');
return $user['id'];
}
//populate created_by and modified_by
public function beforeSave($options = array()) {
parent::beforeSave($options);
//find all fields from table created_by/modified_by exists
$fields = array_keys($this->getColumnTypes());
//get modal name to feed in data in appropriate array key
$modal = array_keys($this->data);
$modal = $modal[0];
//add created_by value
if(in_array('created_by', $fields) && !isset($this->data[$modal]['id'])){
//correct this line
$this->data[$modal]['created_by'] = $this->getCurrentUser()==null?-1:$this->getCurrentUser();
return true;
}elseif(in_array('modified_by', $fields)){
$this->data[$modal]['modified_by'] = $this->getCurrentUser()==null?-1:$this->getCurrentUser();
return true;
}
return true;
}
}

salesforce SOQL : query to fetch all the fields on the entity

I was going through the SOQL documentation , but couldn't find query to fetch all the field data of an entity say , Account , like
select * from Account [ SQL syntax ]
Is there a syntax like the above in SOQL to fetch all the data of account , or the only way is to list all the fields ( though there are lot of fields to be queried )
Create a map like this:
Map<String, Schema.SObjectField> fldObjMap = schema.SObjectType.Account.fields.getMap();
List<Schema.SObjectField> fldObjMapValues = fldObjMap.values();
Then you can iterate through fldObjMapValues to create a SOQL query string:
String theQuery = 'SELECT ';
for(Schema.SObjectField s : fldObjMapValues)
{
String theLabel = s.getDescribe().getLabel(); // Perhaps store this in another map
String theName = s.getDescribe().getName();
String theType = s.getDescribe().getType(); // Perhaps store this in another map
// Continue building your dynamic query string
theQuery += theName + ',';
}
// Trim last comma
theQuery = theQuery.subString(0, theQuery.length() - 1);
// Finalize query string
theQuery += ' FROM Account WHERE ... AND ... LIMIT ...';
// Make your dynamic call
Account[] accounts = Database.query(theQuery);
superfell is correct, there is no way to directly do a SELECT *. However, this little code recipe will work (well, I haven't tested it but I think it looks ok). Understandably Force.com wants a multi-tenant architecture where resources are only provisioned as explicitly needed - not easily by doing SELECT * when usually only a subset of fields are actually needed.
You have to specify the fields, if you want to build something dynamic the describeSObject call returns the metadata about all the fields for an object, so you can build the query from that.
I use the Force.com Explorer and within the schema filter you can click the checkbox next to the TableName and it will select all the fields and insert into your query window - I use this as a shortcut to typeing it all out - just copy and paste from the query window. Hope this helps.
In case anyone was looking for a C# approach, I was able to use reflection and come up with the following:
public IEnumerable<String> GetColumnsFor<T>()
{
return typeof(T).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
.Where(x => !Attribute.IsDefined(x, typeof(System.Xml.Serialization.XmlIgnoreAttribute))) // Exclude the ignored properties
.Where(x => x.DeclaringType != typeof(sObject)) // & Exclude inherited sObject propert(y/ies)
.Where(x => x.PropertyType.Namespace != typeof(Account).Namespace) // & Exclude properties storing references to other objects
.Select(x => x.Name);
}
It appears to work for the objects I've tested (and matches the columns generated by the API test). From there, it's about creating the query:
/* assume: this.server = new sForceService(); */
public IEnumerable<T> QueryAll<T>(params String[] columns)
where T : sObject
{
String soql = String.Format("SELECT {0} FROM {1}",
String.Join(", ", GetColumnsFor<T>()),
typeof(T).Name
);
this.service.QueryOptionsValue = new QueryOptions
{
batchsize = 250,
batchSizeSpecified = true
};
ICollection<T> results = new HashSet<T>();
try
{
Boolean done = false;
QueryResult queryResult = this.service.queryAll(soql);
while (!finished)
{
sObject[] records = queryResult.records;
foreach (sObject record in records)
{
T entity = entity as T;
if (entity != null)
{
results.Add(entity);
}
}
done &= queryResult.done;
if (!done)
{
queryResult = this.service.queryMode(queryResult.queryLocator);
}
}
}
catch (Exception ex)
{
throw; // your exception handling
}
return results;
}
For me it was the first time with Salesforce today and I came up with this in Java:
/**
* #param o any class that extends {#link SObject}, f.ex. Opportunity.class
* #return a list of all the objects of this type
*/
#SuppressWarnings("unchecked")
public <O extends SObject> List<O> getAll(Class<O> o) throws Exception {
// get the objectName; for example "Opportunity"
String objectName= o.getSimpleName();
// this will give us all the possible fields of this type of object
DescribeSObjectResult describeSObject = connection.describeSObject(objectName);
// making the query
String query = "SELECT ";
for (Field field : describeSObject.getFields()) { // add all the fields in the SELECT
query += field.getName() + ',';
}
// trim last comma
query = query.substring(0, query.length() - 1);
query += " FROM " + objectName;
SObject[] records = connection.query(query).getRecords();
List<O> result = new ArrayList<O>();
for (SObject record : records) {
result.add((O) record);
}
return result;
}
I used following to get complete records-
query_all("Select Id, Name From User_Profile__c")
To get complete fields of record, we have to mention those fields as mentioned here-
https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select.htm
Hope will help you !!!

How to change and entity type in Doctrine2 CTI Inheritance

How (if possible at all) do you change the entity type with Doctrine2, using it's Class Table Inheritance?
Let's say I have a Person parent class type and two inherited types Employe and Client. My system allows to create a Person and specify it's type - that's fairly easy to implement - but I'd also like to be able to change the person from an Employe to a Client, while maintaining the Person-level information (it's id and other associated records).
Is there a simple way to do this with Doctrine2?
I was looking for this behaviour yesterday also.
In the end, after speaking with people in #doctrine on freenode, I was told that it is not possible.
If you want to do this, then you have to go through this:
Upgrading a User
Grab the Person Entity.
Update the discrimator column so that it is no longer a 'person' and change it to 'employee'
Create a corresponding row inyour Employee table for this inheritance.
Removing Inheritance
Likewise if you want to remove inheritance, you have to..
Grab the Person Entity.
Update the discrimnator column so that it is no longer an 'employee' and change it to a 'person'.
Delete the corresponding row in your Employee table. (Yes you have to delete it, just change the discrimator coumn is not sufficient).
This might be 7 months late, but it is at least the correct answer for anything else looking to suport such a feature.
PHP doesn't have support for object casting, so Doctrine doesn't support it. To workaround the problem I write this static method into parent classes:
public static function castToMe($obj) {
$class = get_called_class();
$newObj = New $class();
foreach (get_class_vars(get_class($newObj)) as $property => $value) {
if (method_exists($obj, 'get' . ucfirst($property)) && method_exists($newObj, 'set' . ucfirst($property))) {
$newObj->{'set' . ucfirst($property)}($obj->{'get' . ucfirst($property)}());
}
}
return $newObj;
}
You can create this method in class Person and use it to cast from Employe to Client and viceversa:
$employe = New Employe();
$client = Client::castToMe($employe);
Now, if you want, you can remove the $employe entity.
You could do something like this though:
This Trait can be used on your Repository class:
namespace App\Doctrine\Repository;
trait DiscriminatorTrait
{
abstract public function getClassMetadata();
abstract public function getEntityManager();
private function updateDiscriminatorColumn($id, $class)
{
$classMetadata = $this->getClassMetadata();
if (!in_array($class, $classMetadata->discriminatorMap)) {
throw new \Exception("invalid discriminator class: " . $class);
}
$identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];
$column = $classMetadata->discriminatorColumn["fieldName"];
$value = array_search($class, $classMetadata->discriminatorMap);
$connection = $this->getEntityManager()->getConnection();
$connection->update(
$classMetadata->table["name"],
[$column => $value],
[$identifier => $id]
);
}
}
There still might be some extra work you need to put in, like clearing values in fields that are only present on one of your sub-classes
In Doctrine2, when you have your parent entity class, Person set as:
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee", , "client" = "Client"})
*/
class Person
{
// ...
}
and sub classes such as Client set as:
/** #Entity */
class Client extends Person
{
// ...
}
when you instantiate Person as:
$person = new Person();
Doctrine2 checks your #DiscriminatorMap statement (above) for a corresponding mapping to Person and when found, creates a string value in the table column set in #DiscriminatorColumn above.
So when you decide to have an instance of Client as:
$client = new Client();
Following these principles, Doctrine2 will create an instance for you as long as you have declared the parameters in the #DiscriminatorMap. Also an entry will be made on the Person table, in the discr column to reflect that type of entity class that has just been instantiated.
Hope that helps. It's all in the documentation though
i use this method
trait DiscriminatorTrait
{
// ...
public function updateDiscriminatorColumn($id, $class)
{
// ... other code here
$connection->update(
"Person", // <-- just there i put my parent class
[$column => $value],
[$identifier => $id]
);
}
}
and i use call like this after :
$this->em->getRepository(Client::class)->updateDiscriminatorColumn($cCenter->getId(), Employe::class);
$this->em->close();
// I update the data directly without going through doctrine otherwise it will create a new Person
try {
$query = "
INSERT INTO Employe (id, /* ... other fields */)
VALUES ({$callCenter->getId()}, /* ... other fields */)
";
$results = $this->connection->executeQuery($query)->execute();
} catch (\Exception $exception) {
echo $exception->getMessage().PHP_EOL;
}
$this->em->close();
// i restart the connection
/** #var EntityManagerInterface $entityManager */
$entityManager = $this->em;
if ($this->em->isOpen() === false) {
$this->em = $entityManager->create(
$this->em->getConnection(),
$this->em->getConfiguration(),
$this->em->getEventManager()
);
}
// and there a get Employer en update him
$employe = $this->em->getRepository(Employe::class)->find($id);
$employe->setFirstname($callCenter->getFirstName());
// other code
And it is work for me

Resources