OOP (beneficial usage) - php

for my question on how to use OOP in a beneficial way I assume as an example a BASKET to which its owner (Tom) having a certain ADDRESS (NY) can add ARTICLES (Bike, Car). Finally a BILL is printed containg all these information.
My problem is: How to handle collecting the information desired (here: owner, city, amount of items) from several objects? Because I think it is stupid to do this manually as done below (see 4.), isn't it? (even more since the amount of information increases in reality)
So what is the "clean way" for creating the bill / collecting the information needed in this example?
<?php
$a = new basket('Tom','NY');
$a->add_item("Bike",1.99);
$a->add_item("Car",2.99);
$b = new bill( $a );
$b->do_print();
1.
class basket {
private $owner = "";
private $addr = "";
private $articles = array();
function basket( $name, $city ) {
// Constructor
$this->owner = $name;
$this->addr = new addresse( $city );
}
function add_item( $name, $price ) {
$this->articles[] = new article( $name, $price );
}
function item_count() {
return count($this->articles);
}
function get_owner() {
return $this->owner;
}
function get_addr() {
return $this->addr;
}
}
2.
class addresse {
private $city;
function addresse( $city ) {
// Constructor
$this->city = $city;
}
function get_city() {
return $this->city;
}
}
3.
class article {
private $name = "";
private $price = "";
function article( $n, $p ) {
// Constructor
$this->name = $n;
$this->price = $p;
}
}
4.
class bill {
private $recipient = "";
private $city = "";
private $amount = "";
function bill( $basket_object ) {
$this->recipient = $basket_object->get_owner();
$this->city = $basket_object->get_addr()->get_city();
$this->amount = $basket_object->item_count();
}
function do_print () {
echo "Bill for " . $this->recipient . " living in " . $this->city . " for a total of " . $this->amount . " Items.";
}
}

If you do Tell Dont Ask, you would indeed add a render method to the bill to which you would pass an instance of BillRenderer. Bill would then tell BillRenderer how to render the Bill. This is in accordance with InformationExpert and High Cohesion principles that suggest methods to be on the objects with the most information to fulfill the task.
class Bill
{
…
public function renderAs(BillRenderer $billRenderer)
{
$billRenderer->setRecipient($this->owner);
$billRenderer->setAddress($this->address);
…
return $billRenderer->render();
}
}
BillRenderer (an interface) would then know the output format, e.g. you'd write concrete renderers for PlainText or HTML or PDF:
class TxtBillRenderer implements BillRenderer
{
…
public function render()
{
return sprintf('Invoice for %s, %s', $this->name, $this->address);
}
}
echo $bill->renderAs(new TxtBillRenderer);
If your Bill contains other objects, those would implement a renderAs method as well. The Bill would then pass the renderer down to these objects.

Both basket as well as bill could have a relation to a positions item - an object representing an ordered list of zero or more items with a count and price.
As such a list is an object of it's own it's easy to pass around:
$bill = new Bill($buyer, $address, $basket->getPositions());
However the printing of the bill should be done by the BillPrinter, because it's not the job of the bill to print itself:
$billPrinter = new BillPrinter($bill, $printerDevice);
$billPrinter->print();

First of all , in PHP5 the constructor it public function __construct(). What you are using there is the PHP4 way. And then ther eare other issues with your code:
instead of passing the name of the city to the Basket ( do you mean Cart ?), you should be creating the address object instance and passing it.
do not add items based on name an amount of money to the basket, instead add the whole instance of item, otherwise you will have a lot of problems when switching site language or currency.
the Articles (do you mean Items ?) should be created based on ID, not based on name. The reasons for that are the same as above + you will have issues with uniqueness. And then some of items might have lower price, when bought in combination. You need a way to safely identify them.
As for cleaning up the code there:
you should stop creating instance from given parameters in the constructor. While it is not always a bad thing, in your case you are making a mess there.
Bill should not be responsible for printing itself.
Something like :
class Basket
{
// -- other code
public function handleInvoice( Bill $invoice )
{
$invoice->chargeFor( $this->items );
$invoice->chargeTo( $this->account );
return $invoice->process();
}
}
.. then use it as
$cart = new Basket(..);
// some operation with it
$invoice = new Bill;
$cart->handleInvoice($invoice);
$printer = new PDFPrinter;
// OR new JpegPrinter; OR new FakePrinter OR anything else
$printer->print( $invoice );
This would give you an instance of Bill outside the class which then you can either print or send to someone.
Also , you might benefit from watching the willowing lecture:
Inheritance, Polymorphism, & Testing
Don't Look For Things!
Clean Code I: Arguments

Related

Why does my class constructor feels bloat and faulty?

I'm fairly new to OOP and I'm struggling with setting up my classes. I'm working on a project where I have the following classes: dogTag, dog, user.
This is my current dogTagclass:
class DogTag
{
const POST_TYPE = 'dog-tag';
private ?int $id = null;
private string $number;
private ?Dog $dog;
private ?User $user;
private bool $isActive = false;
private bool $isLost = false;
private string $generationDate;
private ?string $activationDate;
private string $productionStatus;
public function __construct($id = null, string $number, ?Dog $dog = null, ?User $user = null, bool $isActive, bool $isLost, string $generationDate, ?string $activationDate, string $productionStatus)
{
$this->id = $id;
$this->number = $number;
$this->dog = $dog;
$this->user = $user;
$this->isActive = $isActive;
$this->isLost = $isLost;
$this->generationDate = $generationDate;
$this->activationDate = $activationDate;
$this->productionStatus = $productionStatus;
}
Let me explain the functionality of the dog tag. Dog Tags are being generated by a generator and stored into the database. So a dog tag only needs the following arguments when being generated: number (this is a unique generated number and not a unique database ID), isActive, isLost (could be optional), generationDate, productionStatus. All other arguments are not necessarily needed for the generation.
So my first question is: "Do I need to set the optional arguments in the contructor?".
Let me explain why I did this for the moment. When a user receives the unique dog tag number they can activate the dog tag. Therefor I use a method called activateDogTag. For a dog tag to be activated it needs to be attached to a user. Therefor I use the $user argument in the constructor.
Here comes my second question: "Should I use a method setUser(User $user) where I inject the user object into the dogtag class?".
Another problem that I'm struggling with is that I also use an id argument. This will be a bit harder to explain why I'm using this and why it feels wrong.
I'm also using the dogTag class to I can instantiate it with all the data from the database. For the moment I have a static DogTag::get($id) method. This class grabs the data from the database by the ID an instantiates a new DogTag class where I fill all the arguments with the data received from the database.
For information: I'm using WordPress)
public static function get($postId): ?DogTag
{
$post = get_post($postId);
if (empty($post)) {
return null;
}
$dogTagId = $post->ID;
$generationDate = $post->post_date;
$dogTagNumber = get_post_meta($post->ID, 'dog_tag_number', true);
$dogId = get_post_meta($post->ID, 'dog_id', true);
if (empty($dogId)) {
$dog = null;
} else {
$dog = Dog::get($dogId);
}
$userId = $post->post_author;
$user = $userId ? User::get($userId) : null;
$isActive = get_post_meta($dogTagId, 'dog_tag_is_active', true);
$isLost = get_post_meta($dogTagId, 'dog_tag_is_lost', true);
$activationDate = get_post_meta($dogTagId, 'dog_tag_activation_date', true);
$productionStatus = get_post_meta($dogTagId, 'dog_tag_production_status', true);
$dogTag = new self($dogTagId, $dogTagNumber, $dog, $user, $isActive, $isLost,
$generationDate, $activationDate, $productionStatus);
$dogTag->post = get_post($postId);
return $dogTag;
}
This way all the arguments are filled and are available within the class.
So where am I going wrong and what I'm I doing correct? The problem is that I can't really find any solution in my online courses for these problems as they don't go this deep.
Where I'm also struggling is eg with the static activation class (DogTag::activateDogTag):
public static function activateDogTag()
{
// Here goes the form
$dogTag = self::getDogTagByDogTagNumber('4OV9NHOPXLAB6X9B');
// $dogTag = self::getDogTagByDogTagNumber('VBD6JZODTZ6L4YU7');
if (!$dogTag) {
var_dump('no dog tag with this ID found');
}
if(true == $dogTag->getIsActive()) {
var_dump('Dog Tag is already active');
}
if ($dogTag->getUser()) {
var_dump('user is set');
return;
}
var_dump('Activate');
$userId = get_current_user_id();
if (0 === $userId) {
var_dump('not logged in as a user');
}
$dogTag->setIsActive(true);
$dogTag->setUser($userId);
$user = $dogTag->getUser();
var_dump($dogTag);
// Update dog Tag
$args = [
'post_type' => 'dog-tag',
'post_status' => 'publish',
'post_title' => $dogTag->getNumber(),
'post_date' => $dogTag->generationDate,
'post_author' => $dogTag->getUser()->getId(),
'meta_input' => [
'dog_tag_number' => $dogTag->getNumber(),
'dog_tag_is_active' => $dogTag->getIsActive(),
'dog_tag_is_lost' => $dogTag->getIsLost(),
'dog_tag_production_status' => $dogTag->getProductionStatus(),
]
];
var_dump($args);
$postId = wp_update_post($args);
$dogTag->setId($postId);
return $dogTag;
}
Is this a good practice:
$dogTag->setIsActive(true);
$dogTag->setUser($userId);
$user = $dogTag->getUser();
and after the data is stored into the database via wp_update_post by setting the id property via DogTag->setId($postId)?
First of all, if it works then you are not doing anything wrong.
Question 1
It's usually better practice to start with required parameters, followed by optional parameters, if it feels too bloated you could remove all optional parameters from the constructor and create setters for those.
ID/Fetching
What you have here is fine, you could create another class DogTagFactory/DogTagRepository which handles the creation/database process for you if you and reduce bloat.
Overall try not to get too stuck in the chase of "perfect" best practice, it's always going to depend on the situation and usually in the end it does not even matter all that much.

Return unique id when use INSERT and SELECT in one moment

i am writing a program using mvc , and because the program i am writing is for admitting bank transactions, the resaults this program gives me is very important to me. and i need this to work without any logical errors and everything should workout with needed focus and giving me the exact thing i expect it to give. the thing that i want it to do for me is that some information will be sent out to the program with web service (like price, info, etc...) and then using a controller it checks the information and calls for the module, and sends the information to it. the it puts the module information to a stdclass and then using the PDO module it creates a transaction. and while the transaction is at work an insert command is sent for the database, and then we have a SELECT command and the program works well even after the insert command but while SELECT is at work , we have repetitive IDs which is the problem here:
my code:
//WSDL Creattor And Class
<?php
ini_set("soap.wsdl_cache_enabled", 1);
set_time_limit(300);
class wsdl extends Controller
{
public function index()
{
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') {
$servidorSoap = new SoapServer('http://' . $_SERVER['SERVER_NAME'] . '/wsdl');
$servidorSoap->setClass('wsdl');
$servidorSoap->handle();
}
}
public function request($pin = null, $amount = 1, $callback = null, $orderid = null, $desc = null)
{
if(empty($callback))
return -10; // call back is empty.
if (empty($pin) && $result->pin != $pin)
return -1; // pin invalid
$this->Model("payment");
$au = $this->mod->Request($result, $amount, $callback, $orderid, $desc);
return $au;
}
}
payment model:
<?php
class Payment_mod extends Model
{
public function Request($gateway, $amount, $callback, $orderid, $description)
{
// using pdo module
$this->DB->CONNECT->beginTransaction();
try {
$trans = new stdClass;
$trans->gateway_id = $gateway->id;
$trans->au = str_replace('.', '_temp_', microtime(true));
$trans->price = $amount;
$trans->order_id = $orderid;
$trans->ip = $_SERVER['REMOTE_ADDR'];
$trans->time = time();
$trans->call_back = $callback;
$trans->description = $description;
$this->DB->Insert("trans", $trans); // insert into table trans …
$id = $this->DB->Fetch("trans", "`gateway_id` =" . $trans->gateway_id . " AND `price`=".$trans->price." AND time = " . $trans->time)->id; // select id from trans where …
$u = new stdClass;
$u->t_id = $id;
$this->DB->Insert("test",$u); // insert into test . it for test table for check result
$this->DB->CONNECT->commit();
} catch (Exception $e) {
$this->DB->CONNECT->rollBack();
return -9; // return unknow error.
}
return $id;
}
}
for understanding where the problem exactly is and how many times each ID repeats itself, i added a table and i got the amount of return data of SELECT and put it into the database. and when i hold the f5 key for some seconds it shows that each ID repeats nearly 9 times.
screenshot:
http://uploads.im/biWEn.jpg
note: this picture shows the amount of repeat of IDs.
where is the problem? i need this to repeat a specific ID for each call.
i did the exact thing in YII framework and i had no problem there and the progam worked exactly as i needed it to. i'd be glad if you help me.

How to dynamically change table schemas for multi-tenant databases in Doctrine 2/PHP 5.4

WHAT I'M TRYING TO DO:
So I'm trying to implement a multi-tenant database architecture using SQL Azure, PHP 5.4, Zend Framework 2, and Doctrine 2. I'm going with the "Shared Database, Separate Schemas" architecture as mentioned in this article: http://msdn.microsoft.com/en-us/library/aa479086.aspx
Unlike simple multi-tenant environments, my environment has certain use cases where a User from Tenant A should be able to access information from a table in Tenant B. So because of this there are "root" or "global" tables that aren't made for each tenant and are instead used by all tenants. So as an example, I could have a table called users that exists for each tenant each with a unique schema name (e.g. tenanta.users and tenantb.users). I would then also have a root schema for things like global permissions (e.g. root.user_permissions).
WHAT I'VE DONE:
In Module.php's onBootstrap() function I've set up a loadClassMetadata event for dynamically changing the schemas of tables, like so:
$entityManager = $serviceManager->get('doctrine.entitymanager.orm_default')->getEventManager();
$entityManager->addEventListener(array( \Doctrine\ORM\Events::loadClassMetadata ), new PrependTableEvent() );
The PrependTableEvent object uses session data to know which schema to use, it looks like so:
namespace Application\Model;
use Zend\Session\Container;
class PrependTableEvent {
private $session;
public function __construct() {
$this->session = new Container('base');
}
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs) {
$classMetadata = $eventArgs->getClassMetadata();
$table = $classMetadata->table;
$table_name = explode('.', $table['name']);
if ( 'root' != $table_name[0] && NULL !== $this->session->queryschema ) {
$table['name'] = $this->session->queryschema . '.' . $table_name[1];
}
$classMetadata->setPrimaryTable($table);
}
}
In order for loadClassMetadata to be called everytime the queryschema changes I built a QuerySchemaManager that looks like so:
namespace Application\Model;
use Doctrine\ORM\Events,
Doctrine\ORM\Event\LoadClassMetadataEventArgs,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\EntityManager,
Zend\Session\Container;
class QuerySchemaManager {
private static $session;
private static $initialized = FALSE;
private static function initialize() {
QuerySchemaManager::$session = new Container('base');
QuerySchemaManager::$initialized = TRUE;
}
public static function reload_table_name( EntityManager $em, $class, $schema) {
if ( ! QuerySchemaManager::$initialized ) {
QuerySchemaManager::initialize();
}
QuerySchemaManager::$session->queryschema = $schema;
if ($em->getEventManager()->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new LoadClassMetadataEventArgs($em->getClassMetadata($class), $em);
$em->getEventManager()->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
}
public static function reload_all_table_names( EntityManager $em, $schema) {
if ( ! QuerySchemaManager::$initialized ) {
QuerySchemaManager::initialize();
}
QuerySchemaManager::$session->queryschema = $schema;
if ($em->getEventManager()->hasListeners(Events::loadClassMetadata)) {
$metadatas = $em->getMetadataFactory()->getAllMetadata();
foreach($metadatas as $metadata) {
$eventArgs = new LoadClassMetadataEventArgs($metadata, $em);
$em->getEventManager()->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
}
}
}
All that code works great and properly updates the ClassMetadata files for each entity.
THE PROBLEM:
I have an issue with Doctrine 2 where when I insert values into a table for Tenant A and then try to insert values into the same table for Tenant B, all the rows get inserted into Tenant A's table.
I spent a lot of time following break points to find the problem, but I still have no idea how to solve it.
(Note: All the following Code is from Doctrine, so it can't/shouldn't be edited by me)
The problem is that EntityManager->unitOfWork has a private array called $persisters that stores (in my case) BasicEntityPersister objects. Every time one of the BasicEntityPersisters are needed UnitOfWork's getEntityPersister($entityName) is called which looks like so:
public function getEntityPersister($entityName)
{
if ( ! isset($this->persisters[$entityName])) {
$class = $this->em->getClassMetadata($entityName);
if ($class->isInheritanceTypeNone()) {
$persister = new Persisters\BasicEntityPersister($this->em, $class);
} else if ($class->isInheritanceTypeSingleTable()) {
$persister = new Persisters\SingleTablePersister($this->em, $class);
} else if ($class->isInheritanceTypeJoined()) {
$persister = new Persisters\JoinedSubclassPersister($this->em, $class);
} else {
$persister = new Persisters\UnionSubclassPersister($this->em, $class);
}
$this->persisters[$entityName] = $persister;
}
return $this->persisters[$entityName];
}
So it will create one BasicEntityPersister per entity (i.e. Application\Model\User will have one BasicEntityPersister even though its schema name will dynamically change), which is fine.
Each BasicEntityPersister has a private member called $insertSql which stores the insert SQL statement once it has been created. When the insert statement is needed this method is called:
protected function getInsertSQL()
{
if ($this->insertSql !== null) {
return $this->insertSql;
}
$columns = $this->getInsertColumnList();
$tableName = $this->quoteStrategy->getTableName($this->class, $this->platform);
if (empty($columns)) {
$identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform);
$this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn);
return $this->insertSql;
}
$values = array();
$columns = array_unique($columns);
foreach ($columns as $column) {
$placeholder = '?';
if (isset($this->class->fieldNames[$column])
&& isset($this->columnTypes[$this->class->fieldNames[$column]])
&& isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion'])) {
$type = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]);
$placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);
}
$values[] = $placeholder;
}
$columns = implode(', ', $columns);
$values = implode(', ', $values);
$this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values);
return $this->insertSql;
}
These three lines are the culprit:
if ($this->insertSql !== null) {
return $this->insertSql;
}
If those lines were commented out then it would work perfectly as the metadata it uses to create the insertSql statement updates properly. I can't find a way to delete/overwrite the insertSql variable, or to even delete/overwrite the whole BasicEntityPersister.
Anyone who's implemented a multi-tenant environment using Doctrine 2 I would like to know how you did it. I don't mind redoing all or large parts of my work, I just need to know what the best way to go about doing this is. Thanks in advance.
You are fighting against the ORM (which is meant to be generating the SQL for you). This should be a signal that you perhaps are going about things in the wrong way.
Rather than modify the persisters (that generate the SQL strings) you should be adding an additional EntityManager. Each entity EntityManager (and therefore UnitOfWork) are designed to persist to one schema; so your second one would simple handle the persistence to the second database - No need to change Doctrine internals!
I have not personally tried to connect to two schemas; however reading into it it seems that it should be possible with the DoctrineModule v1.0.

PHP e-commerce system circular dependency issue

I'm just getting started with dependency injection and I have immediately hit a problem: I have two classes that depend on each other.
The classes are Basket and Shipping.
In my Basket class I have the following relevant methods:
public function totalShipping()
{
return $this->_shipping->rate();
}
public function grandTotal()
{
return $this->totalProductsPrice() + $this->totalShipping();
}
public function totalWeight()
{
$weight = 0;
$products = $this->listProducts();
foreach ($products as $product) {
$weight += $product['product_weight'];
}
return ($weight == '') ? 0 : $weight;
}
$this->_shipping is an instance of the Shipping class
In my Shipping class I have the following relevant methods:
public function rate()
{
if (isset($_SESSION['shipping']['method_id'])) {
$methodId = $_SESSION['shipping']['method_id'];
return $this->_rates[$methodId]['Shipping Price'];
}
// Method not set
return NULL;
}
// Available Methods depend on country and the total weight of products added to the customer's basket. E.g. USA and over 10kg
public function listAvailableMethods()
{
$rates = array();
if (isset($_SESSION['customer']['shipping_address']['country_code'])) {
foreach ($this->_rates as $method_id => $rate) {
if (($_SESSION['customer']['shipping_address']['country_code'] == $rate['Country']) && ($this->_basket->totalWeight() > $rate['Weight From']) && ($this->_basket->totalWeight() < $rate['Weight To'])) {
$rates[$method_id] = $rate;
}
}
}
return $rates;
}
$this->_basket is an instance of the Basket class.
I am totally clueless as to how to resolve this circular dependency. Thank you for your help in advance.
Update
In my Shipping Class I also have this method:
public function setMethod($method_id)
{
// A check to make sure that the method_id is one of the provided methods
if ( !array_key_exists($method_id, $this->listAvailableMethods()) ) return false;
$_SESSION['shipping'] = array(
'method_id' => $method_id
);
}
I have ended up renaming Shipping to Shipping_Methods and I have created a new class called Customer_Shipping_Methods. Essentially Customer_Shipping_Methods could be part of the Basket class but I'd rather keep it separate.
#RyanLaBarre was totally right. I was essentially mixing methods that should have been in the Basket Class with methods in my Shipping_Methods class. Shipping_Methods should have only contained general shipping data methods which were not specific to the current session.
I think what threw me, was that Shipping_Methods sources its data from a csv file rather than a database table. Once I started seeing Shipping_Methods as just another table, it all clicked in my mind.
#rdlowrey that is very good advice. I will be putting my session globals into my controllers immediately!

OOP PHP with FB Graph API

I am creating a Student object using OOP in PHP via the Facebook Graph API. My problem is that not all users share the same amount of data on FB so if a particular user doesn't list certain variables instantiated in the object, I get undefined variable messages. What is the best way to prepare for this, i.e. create an object whether or not the user has shared all of the data in the object or not? Code below:
<?php
require_once('class.Student.php');
$name = $user_profile['name'];
$hometown = $user_profile['hometown']['name'];
$location = $user_profile['location']['name'];
$birthday = $user_profile['birthday'];
$highschool = $user_profile['education'][0]['school']['name'];
$hsgrad = $user_profile['education'][0]['year']['name'];
$collegeid = $user_profile['education'][1]['school']['id'];
$college = $user_profile['education'][1]['school']['name'];
$majorid = $user_profile['education'][1]['concentration'][0]['id'];
$major = $user_profile['education'][1]['concentration'][0]['name'];
$grad = $user_profile['education'][1]['year']['name'];
$company = $user_profile['work'][0]['employer']['name'];
$jobtitle = $user_profile['work'][0]['position']['name'];
$startdate = $user_profile['work'][0]['start_date'];
$interest = $interests['data'][0]['name'];
$interestid = $interests['data'][0]['id'];
$objStudent = new Student($name,$hometown,$location,$birthday,$highschool,$hsgrad,$collegeid,$college,$majorid,$major,$grad,$company,$jobtitle,$startdate,$interest,$interestid);
?>
And the class itself:
<?php
class Student {
public $name;
public $hometown;
public $location;
public $birthday;
public $highschool;
public $hsgrad;
public $collegeid;
public $college;
public $majorid;
public $major;
public $grad;
public $company;
public $jobtitle;
public $startdate;
public $interest;
public $interestid;
public function __construct($name,$hometown,$location,$birthday,$highschool,$hsgrad,$collegeid,$college,$majorid,$major,$grad,$company,$jobtitle,$startdate,$interest,$interestid) {
$this->name = $name;
$this->hometown = $hometown;
$this->location = $location;
$this->birthday = $birthday;
$this->highschool = $highschool;
$this->hsgrad = $hsgrad;
$this->collegeid = $collegeid;
$this->college = $college;
$this->majorid = $majorid;
$this->major = $major;
$this->grad = $grad;
$this->company = $company;
$this->jobtitle = $jobtitle;
$this->startdate = $startdate;
$this->interest = $interest;
$this->interestid = $interestid;
}
I understand how to handle this in the functions of the class, using a simple isset such as:
function goalRecommender () {
if (isset($this->interest)) {
if ($this->interest =='Computer Science') {
echo "<p>Based on your interest in Computer Science, we recommend working in the software industry. Furthermore, your interest in user interface design would be thoroughly put to use at Facebook, one of the fastest growing technology companies in the world. If you would like to pursue another goal,
search our database to the right or click one of the 'popular goal' links.<p>";
}
elseif ($this->interests =='Finance') {
echo "<p>You're interested in Finance</p>";
}
elseif ($this->interests =='Film') {
echo "<p>You're interested in Film</p>";
}
elseif ($this->interests =='Marketing') {
echo "<p>You're interested in Marketing</p>";
} else {
echo "<p>Choose a goal.</p>";
}
} else {
echo "<p>What are you interested in?
<select>
<option>Finance</option>
<option>Marketing</option>
<option>Film</option>
<option>Software</option>
</select></p>";
}
}
But I'm just learning OOP in PHP and am unsure how to do it when instantiating an object. Any guidance would be sincerely appreciated.
There's a lot for your class:
Have your Student constructor take an array. This will reduce the pain of variable adjustments, especially if you end up extending Student.Clarification: rather than passing in the name, college, hometown, birthday, etc, you pass in an array that has all that same information.
$name = $user_profile['name'];
$hometown = $user_profile['hometown']['name'];
//becomes
$user['name'] = $user_profile['name'];
$user['hometown'] = $user_profile['hometown']['name'];
It's faster. The cost of any parameter isn't much, but when you have tons like this it can at least make a mark.
It's easier to call. You no longer have to remember the order of variables. "Is it name, hometown, location, birthday? Or name, birthday, location, hometown?" You have tons of variables, there's no way to easily remember that.
It's easier to change. Adding a variable? No problem, add it to the array and check for it inside the function. Removing a variable? That's hard to do, but not if it's in an array.
public function __construct(
$name,
$hometown,
$location,
$birthday,
$highschool,
$hsgrad,
$collegeid,
$college,
$majorid,
$major,
$grad,
$company,
$jobtitle,
$startdate,
$interest,
$interestid) {
//becomes
public function __construct(array $info) {
Your variables should be protected or private, not public. This is pretty much industry standard.
You'll want to provide getter and setter methods because the variables are inaccessible. Many IDE's can do this for you.
As for your error messages:
You need to check to make sure the variable exists before you actually access it. Use the isset() function for this:
$hometown = isset($user_profile['hometown']['name']) ? $user_profile['hometown']['name'] : null;

Categories