Optional setter - php

How can I set a specific setter inside of a child class to be optional
I have a web page build with PHP that prints a recursive objects array, I have a parent class called Person and a child class called Teachers.
Now the teachers can also teach another teacher, I have divided my teachers with the following types "Master, Senior, Junior", and as a matter of fact I already solve that problem, I can see an array of objects within an array of objects, something like this:
Array
(
[0] => Teacher Object
(
[subject:Teacher:private] => Matematicas
[teacherType:Teacher:private] => Master
[teachers:Teacher:private] => Array
(
[0] => Teacher Object
(
[subject:Teacher:private] => Matematicas 2
[teacherType:Teacher:private] => Senior
[teachers:Teacher:private] => Array
(
[0] => Teacher Object
(
[subject:Teacher:private] => Ciancias 1
[teacherType:Teacher:private] => Junior
[teachers:Teacher:private] => Array
(
)
[id:Person:private] => 7
[name:Person:private] => Pedro
[nickname:Person:private] => PedroBoy
)
)
I imagine you get the idea, my problem is that the junior teacher does not receive any teachers array, because he is a jnuior teacher and he can't teach teachers yet. But if I write nothing on it, it will launch an error saying that the teachers array of junior teachers is undifined and it will assign an empty teachers array object to it.
Is there any way to nake that specific setter optional?.
Here is my PHP Code:
class Teacher extends Person{
private $subject = '';
private $teacherType = '';
private $teachers = array();
public function __construct($data) {
parent::__construct($data);
$this->setSubject($data["subject"]);
//OPTIONAL
$this->setTeachers($data["teachers"]);
$this->setTeacherType($data["type"]);
}
?>

Maybe a simple if statement?
class Teacher extends Person
{
private $subject = '';
private $teacherType = '';
private $teachers = array();
public function __construct($data)
{
parent::__construct($data);
$this->setSubject($data["subject"]);
if (isset($data['teachers']))
$this->setTeachers($data["teachers"]);
$this->setTeacherType($data["type"]);
}

Related

Creating array of objects in data mapper pattern

EDIT: OUTPUT CODE AT BOTTOM OF QUESTION
I just posted a question, thinking my problem was the query, but it turns out it's my PHP code.
Here's the problem. I have a GoalChallenge class, which has numerous properties, one of which should be one, or an array of ProductService objects; see GoalChallenge class below (note I have stripped out the other getters and setters, and left the ones that relate to the ProductService class.
When I use GoalChallenge::findByPersonaId, a ProductService object is created and relates to the matching GoalChallenge object, but there should be 2 ProductService objects within the GoalChallenge->product_service property (the query should match 2 rows). Instead, a duplicate GoalChallenge object is created, containing the same property values for everything other than the product_service property, which contains the 2nd matching object from the query.
I need the two matching ProductService objects to be part of the same GoalChallenge object (as matched by the query) - how can I make this happen?
If you need anything else, please ask. Really appreciate any help! Code below;
GoalChallenge.class.php
<?php
class GoalChallenge
{
private $id;
private $persona_id;
private $title;
private $item_category;
private $description;
private $solution;
private $product_service;
private $research_checklist;
private $subtopics;
private $keywords;
private $status;
public function __construct(
$id = null,
$persona_id = null,
$title = null,
$item_category = null,
$description = null,
$solution = null,
ProductService $product_service = null,
$research_checklist = null,
$subtopics = null,
$keywords = null,
$status = null
) {
$this->id = $id;
$this->persona_id = $persona_id;
$this->title = $title;
$this->item_category = $item_category;
$this->description = $description;
$this->solution = $solution;
$this->product_service = $product_service;
$this->research_checklist = $research_checklist;
$this->subtopics = $subtopics;
$this->keywords = $keywords;
$this->status = $status;
}
public function getProductService()
{
return $this->product_service;
}
public function setProductService(ProductService $product_service)
{
$this->product_service = $product_service;
}
}
And my GoalChallengeMapper.class.php;
class GoalChallengeMapper
{
protected $dblayer;
public function __construct(PDO $dblayer)
{
$this->dblayer = $dblayer;
}
public function saveField($id, $field, $data)
{
try {
$this->dblayer->beginTransaction();
$stmt = $this->dblayer->prepare("UPDATE goals_challenges SET $field = :data WHERE id = :id");
$stmt->bindParam(':id', $id);
$stmt->bindParam(':data', $data);
$stmt->execute();
$this->dblayer->commit();
return $stmt->rowCount();
} catch(PDOException $e) {
$this->dblayer->rollBack();
echo $e->getMessage();
exit;
}
}
public function findByPersonaId($persona_id)
{
try {
$this->dblayer->beginTransaction();
$stmt = $this->dblayer->prepare("SELECT goals_challenges.*, products_services.id as psid, products_services.url, products_services.feature_benefit from goals_challenges LEFT JOIN products_services ON goals_challenges.id = products_services.goal_challenge_id WHERE goals_challenges.persona_id = :persona_id");
$stmt->bindParam(':persona_id', $persona_id);
$stmt->execute();
$this->dblayer->commit();
$result_set = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$result_set[] = $this->mapObject($row);
}
return $result_set;
} catch (PDOException $e) {
$this->dblayer->rollBack();
echo $e->getMessage();
exit;
}
}
public function mapObject(array $row)
{
$entry = new GoalChallenge();
$entry->setId($row['id']);
$entry->setPersonaId($row['persona_id']);
$entry->setTitle($row['title']);
$entry->setItemCategory($row['item_category']);
$entry->setDescription($row['description']);
$entry->setSolution($row['solution']);
$entry->setProductService(new ProductService($row['psid'], $row['id'], $row['url'], explode(',', $row['feature_benefit'])));
$entry->SetResearchChecklist($row['research_checklist']);
$entry->setSubtopics($row['subtopics']);
$entry->setKeywords($row['keywords']);
$entry->setStatus($row['status']);
return $entry;
}
}
And finally, my ProductService class (minus getters and setters)
class ProductService
{
private $id;
private $goal_challenge_id;
private $url;
private $feature_benefit = [];
public function __construct($id = null, $goal_challenge_id = null, $url = null, array $feature_benefit = null)
{
$this->id = $id;
$this->goal_challenge_id = $goal_challenge_id;
$this->url = $url;
$this->feature_benefit = $feature_benefit;
}
}
THIS IS THE OUTPUT
GoalChallenge Object
(
[id:GoalChallenge:private] => 173
[persona_id:GoalChallenge:private] => 14
[title:GoalChallenge:private] => Lead Gen
[item_category:GoalChallenge:private] => Business Challenge
[description:GoalChallenge:private] =>
[solution:GoalChallenge:private] => Advertising
[product_service:GoalChallenge:private] => ProductService Object
(
[id:ProductService:private] => 1
[goal_challenge_id:ProductService:private] => 173
[url:ProductService:private] => www.google.com
[feature_benefit:ProductService:private] => Array
(
[0] => good for testing
[1] => mobile
)
)
[research_checklist:GoalChallenge:private] => 0,0,0,0,0,0
[subtopics:GoalChallenge:private] =>
[keywords:GoalChallenge:private] => ,,,,
[status:GoalChallenge:private] => 1
)
GoalChallenge Object
(
[id:GoalChallenge:private] => 173
[persona_id:GoalChallenge:private] => 14
[title:GoalChallenge:private] => Lead Gen
[item_category:GoalChallenge:private] => Business Challenge
[description:GoalChallenge:private] =>
[solution:GoalChallenge:private] => Advertising
[product_service:GoalChallenge:private] => ProductService Object
(
[id:ProductService:private] => 3
[goal_challenge_id:ProductService:private] => 173
[url:ProductService:private] => www.test.com
[feature_benefit:ProductService:private] => Array
(
[0] => good for searching
[1] => well known
)
)
[research_checklist:GoalChallenge:private] => 0,0,0,0,0,0
[subtopics:GoalChallenge:private] =>
[keywords:GoalChallenge:private] => ,,,,
[status:GoalChallenge:private] => 1
)
mysql> SELECT goals_challenges.*, products_services.id as psid, products_services.url, products_services.feature_benefit FROM goals_challenges LEFT JOIN products_services ON goals_challenges.id = products_services.goal_challenge_id WHERE goals_challenges.persona_id = 14;
+-----+------------+----------+--------------------+-------------+-------------+-----------------+--------------------+-----------+----------+--------+------+----------------+--------------------------------+
| id | persona_id | title | item_category | description | solution | product_service | research_checklist | subtopics | keywords | status | psid | url | feature_benefit |
+-----+------------+----------+--------------------+-------------+-------------+-----------------+--------------------+-----------+----------+--------+------+----------------+--------------------------------+
| 173 | 14 | Lead Gen | Business Challenge | | Advertising | NULL | 0,0,0,0,0,0 | NULL | ,,,, | 1 | 1 | www.google.com | good for testing, mobile |
| 173 | 14 | Lead Gen | Business Challenge | | Advertising | NULL | 0,0,0,0,0,0 | NULL | ,,,, | 1 | 3 | www.test.com | good for searching, well known |
+-----+------------+----------+--------------------+-------------+-------------+-----------------+--------------------+-----------+----------+--------+------+----------------+--------------------------------+
2 rows in set (0.00 sec)
print_r($goals_challenges)
Array
(
[173] => Array
(
[id] => 173
[persona_id] => 14
[title] => Lead Gen
[item_category] => Business Challenge
[description] =>
[solution] => Advertising
[research_checklist] => 0,0,0,0,0,0
[subtopics] =>
[keywords] => ,,,,
[status] => 1
[psid] => 1
[url] => www.google.com
[feature_benefit] => good for testing, mobile
[product_services] => Array
(
[0] => Array
(
[0] => 1
[1] => www.google.com
[2] => good for testing, mobile
)
[1] => Array
(
[0] => 3
[1] => www.test.com
[2] => good for searching, well known
)
)
)
)
As suspected, the JOIN query's result set requires a little more logic to format the way you want than you have given it. A SQL result set is always a 2 dimensional structure, even when the data it contains have more complicated relationships (like your one-to-many relationship).
There are a few ways to approach this, and the one I think will be closest to your existing pattern is to change the way you fetch rows a little. Instead of fetching a row then immediately mapping it, build some logic into the fetch loop to create the nested structure your join expresses, wherein ProductService is an array of one or more objects. Then you'll be able to modify the mapObject() method to handle an array of nested ProductService objects.
So instead of mapping as you fetch, create an array onto which the fetched rows are appended. On each iteration, you must check if the common values (those of GoalChallenge) have changed. If not, you continue building an array for ProductService. If they have changed (like if your query returns more than one different GoalChallenge) you start a new outer structure.
$result_set = array();
// Temp variable to remember what goals_challenges.id is being grouped
$current_id = null;
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// Create a new structure if the id changed
if($row['id'] !== $current_id) {
$current_id = $row['id'];
// Store a new row for goal_challenges, holding all
// the common columns in its outer structure
$goal_challenges[$row['id']] = $row;
// and it has a sub-array for product services
$goal_challenges[$row['id']]['product_servies'] = array();
}
// Append the product_services columns as an array onto the subarray
$goal_challenges[$row['id']]['product_services'][] = array('psid'=>$row['psid'], 'url'=>$row['url'], 'feature_benefit'=>$row['feature_benefit']);
}
// Now you can pass each row of the $goal_challenges array
// to mapObject. There should be only one row, but if you
// use the same pattern for queries that return many rows it
// will work without much modification
$result_set = array();
foreach ($goal_challenges as $gc) {
$result_set[] = $this->mapObject($gc);
}
// Return the array of results (which probably has only one element)
return $result_set;
Okay, that should fix the fetch pattern to do what you need. The other issue is to make the mapObject() method handle the inner array of product services. That's easy enough with a loop.
public function mapObject(array $row)
{
$entry = new GoalChallenge();
$entry->setId($row['id']);
$entry->setPersonaId($row['persona_id']);
$entry->setTitle($row['title']);
$entry->setItemCategory($row['item_category']);
$entry->setDescription($row['description']);
$entry->setSolution($row['solution']);
$entry->SetResearchChecklist($row['research_checklist']);
$entry->setSubtopics($row['subtopics']);
$entry->setKeywords($row['keywords']);
$entry->setStatus($row['status']);
// Create ProductService objects for each item in the sub-array
foreach ($row['product_services'] as $ps) {
$entry->setProductService(new ProductService($ps['psid'], $row['id'], $ps['url'], explode(',', $ps['feature_benefit'])));
}
return $entry;
}
And finally, make the setProductService() method append to an array instead of setting a single property:
public function setProductService(ProductService $product_service)
{
// Append onto an array
$this->product_service[] = $product_service;
}
In the GoalChallenge::__construct() parameters, make it accept and default an array instead of a single ProductService object, changing to $product_service = array()
So this is all sort of complicated, and it speaks to why pre-built ORM libraries like Doctrine are commonly used. This logic is abstracted away for you in an easily reusable way. PDO does have a FETCH_GROUP methodology, but it is meant to group just one column (like the id) as an outer array key and all other columns as sub-arrays. Your situation is such that most columns belong at an outer level, with only those relevant to the joined ProductService as an inner sub-array, so that doesn't really work.

symfony propel : wrongly populated object with criteria addJoin

This is my first question here, so please try to be patient with me :)
I've stumbled upon a weird behavior populating an object.
I started to convert the objectQuery::create()-> ... ->find() methods used in my project to $c = new Criteria(), $c-> ... objectPeer::doSelect($c) since I've been told Queries shouldn't be used when criteria can be.
I have a function, that returns all the prices of items from the shop. Or at least did. The thing that I cannot figure out is this:
the old code:
static public function shopGetPrices($id){
$prices = itemPriceQuery::create()->
addJoin(itemPricePeer::ITEM_ID, itemPeer::ID, Criteria::LEFT_JOIN)->
addJoin(itemPeer::CATEGORY_ID, categoryPeer::ID, Criteria::LEFT_JOIN)->
addJoin(categoryPeer::SHOP_ID, shopPeer::ID, Criteria::LEFT_JOIN)->
add(shopPeer::ID, $id)->find();
return $prices;
}
returns correctly populated PropelObjectCollection object, through which i can go with foreach, and get/set the itemPrice objects and attributes i need.
now, the new code:
static public function shopGetPrices($id){
$c = new Criteria();
$c->addJoin(itemPricePeer::ITEM_ID, itemPeer::ID, Criteria::LEFT_JOIN)->
addJoin(itemPeer::CATEGORY_ID, categoryPeer::ID, Criteria::LEFT_JOIN)->
addJoin(categoryPeer::SHOP_ID, shopPeer::ID, Criteria::LEFT_JOIN)->
add(shopPeer::ID, $id);
return self::DoSelect($c);
}
returns an array of itemPrice objects, but they are populated with item values related to itemPrice objects through join. that means : when I call print_r(self::DoSelect($c)); it prints
Array
(
[0] => ItemPrice Object
(
[startCopy:protected] =>
[id:protected] => 47 <- id of joined item
[item_id:protected] => 9 <-foreign key to category object of joined item
[price:protected] => 0
[unit:protected] => Axe <- name of item, not unit (unit is like 'golden', 'iron', 'wood' or whatever )
[active:protected] =>
[collItemsOrder:protected] =>
[collItemsOrderPartial:protected] =>
[alreadyInSave:protected] =>
[alreadyInValidation:protected] =>
[polozkyObjednavkasScheduledForDeletion:protected] =>
[prisadyPolozkyObjednavkasScheduledForDeletion:protected] =>
[validationFailures:protected] => Array()
[_new:protected] =>
[_deleted:protected] =>
[modifiedColumns:protected] => Array()
[virtualColumns:protected] => Array()
)
[1] => ItemPrice Object
(
...and so on.
There is probably some crucial difference between criteria and query object, that I'm missing. I searched on Google, StackOverflow, and who knows where, but I didn't find anything resembling a solution to this.
This guy/gal had a vaguely similar problem, but I didn't use addSelectColumn with my criteria, so it's been another dead end for me.
Can anyone please point me in the right direction?
I found the problem. It was that I had overriden method do select in itemPricePeer class
public static function doSelect(Criteria $criteria, PropelPDO $con = null){
$critcopy = clone $criteria;
$critcopy->add(self::ACTIVE, 1);
return self::populateObjects(itemPeer::doSelectStmt($critcopy, $con));
}
I switched self/itemPricePeer with itemPeer in populateObjects arguments. silly me :-/ Thanks for your responses anyway j0k.

CakePHP query array

I am running this query using CakePHP:
$total = $this->Lapse->query("select sum(unix_timestamp(stop) - unix_timestamp(start)) from lapses where id = ".$lastId."");
And i get back this array structure:
Array
(
[0] => Array
(
[0] => Array
(
[sum(unix_timestamp(stop) - unix_timestamp(start))] => 1
)
)
)
So my variable holds this: $updateVal = $total[0][0][0];
Which isn't the prettiest, is there a way i can simplify this OTT array?
Have you tried the find() method passing a custom fields option?:
$this->Lapse->find('all', array(
'fields' => array('sum(unix_timestamp(stop) - unix_timestamp(start)) as elapsed_time'),
'conditions' => array('Lapse.id' => $lastId),
));
The returned array is prettier than the one you're getting, although it's not prettier than elapsed_time being an actual model property.
Another solution would be to set elapsed_time as a virtual field within the model:
class Lapse extends AppModel {
...
public $virtualFields = array(
'elapsed_time' => 'sum(unix_timestamp(Lapse.stop) - unix_timestamp(Lapse.start)',
);
...
}
Then elapsed_time acts as a model property and would be returned as $updateVal['Lapse']['elapsed_time'] in every find() call.

How do I declare 'sub-objects' in PHP

I'm relatively new to OOP in PHP, and I'm not sure if what I'm trying to do is possible or recommended. In any case, I can't figure it out. I'd appreciate any pointers to tutorials or documents which might help - I'm not expecting a full-blown answer here.
I have a system in which each user has a number of 'Libraries'. Each Library contains a number of 'Elements'.
DB set up is as follows:
user_libraries
- id (unique)
- user_id (identifies user)
- name (just a string)
elements
- id (unique)
- content (a string)
library_elements
- id (unique)
- library_id
- element_id
where library_id is the id from user_libraries, and element_id is that from elements.
I want to be able to access a given user's library, and their elements.
I've set up the library class, and can use it to retrieve the list of libraries (or a sub-list).
I do this like this:
$mylibraryset = new LibrarySet();
$mylibraryset->getMyLibraries();
which gives (when I use print_r):
LibrarySetObject (
[user_id] => 105
[data_array] => Array (
[0] => Array (
[id] => 1
[user_id] => 105
[type] => 1
[name] => My Text Library
)
[1] => Array (
[id] => 2
[user_id] => 105
[type] => 2
[name] => Quotes
)
)
)
Now, what I'd like to be able to do is for each of those libraries (the elements in data_array), to retrieve all the elements.
The best idea I've had so far is to do something like:
foreach($mylibrary->data_array as $library) {
$sublibrary = new Library();
$sublibrary -> getAllElements();
}
where Sublibrary is another class which has the function getAllElements. I can't quite get it to work though, and I'm not sure I'm on the right lines...
Is there a way that I can then end up being able to do something like this:
$mylibrary->sublibraries[0]->element[0]
to retrieve a specific element?
As I say, I don't expect a full-blown explanation here - just pointers to get me started.
<?php
class Library {
public $element;
public $data;
public function __construct($sublibrary) {
$this->data = $sublibrary;
}
public function getAllElements() {
// populate $this->element using $this->data
}
}
class LibrarySet {
public $user_id;
public $data_array;
public $sublibraries;
public function getMyLibraries() {
// populate $this->data_array
$this->sublibraries = Array();
foreach($this->data_array as $index => $sublibrary) {
$this->sublibraries[$index] = new Library($sublibrary);
$this->sublibraries[$index]->getAllElements();
}
}
}
$mylibraryset = new LibrarySet();
$mylibraryset->getMyLibraries();
$mylibraryset->sublibraries[0]->element[0]
?>

Modify PHP Object Property Name

In PHP is it possible to change an Objects property key/name? For example:
stdClass Object
(
[cpus] => 2
[created_at] => 2011-05-23T01:28:29-07:00
[memory] => 256
)
I wish to change the key created_at to created in the Object leaving an object that looks like:
stdClass Object
(
[cpus] => 2
[created] => 2011-05-23T01:28:29-07:00
[memory] => 256
)
$object->created = $object->created_at;
unset($object->created_at);
Something like an adapter class may be a more robust choice though, depending on where and how often this operation is necessary.
class PC {
public $cpus;
public $created;
public $memory;
public function __construct($obj) {
$this->cpus = $obj->cpu;
$this->created = $obj->created_at;
$this->memory = $obj->memory;
}
}
$object = new PC($object);
No, since the key is a reference to the value, and not a value itself.
You're best off copying the original, then removing it.
$obj->created = $obj->created_at;
unset(obj->created_at);
Its similar to #deceze adapter, but without the need to create an extra class
$object = (object) array(
'cpus' => $obj->cpus,
'created' => $obj->created_at,
'memory' => $obj->memory
);

Categories