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;
Related
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.
I'm trying to learn about Object Oriented Programming and I want to turn this code into such. I've got some knowledge so far from google and here and in particular http://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762.
The way I understand it is I need classes that contain a certain set of instructions that can be used with universal objects outside of those classes.
My idea so far has been to set up a User class where names are stored (coming from a HTML/PHP form).
class User {
public $nameStore, $fName, $lName, $email;
public function __construct ($fName, $lName, $email) {
$this->$fN = $fName;
$this->$lN = $lName;
$this->$eN = $email;
}
Like the above^. But I'm still confused about where other instructions of my code should go. That's where I need the most help. From what I've read, it hasn't helped me get the full grasp of what I need to do. If someone could help get me started in the right direction on how to make my code into an OOP type I would greatly appreciate it. Thanks!
Below is my procedural code that I want to convert to OOP.
<?php
session_start();
$formNames = $_POST['names'];
$active = (isset($_POST['activate'])) ? $_POST['activate'] : false;
//checks if activate checkbox is being used
$email = '#grantle.com';
$fullnames = explode(", ", $_POST['names']);
if ($active == true) {
$active = '1';
//sets activate checkbox to '1' if it has been selected
}
/*----------------------Function to Insert User-------------------------*/
function newUser($firstName,$lastName,$emailUser,$active,$conn){
//a function to insert a user into a database is here
}
//newUser function enters names in database
/*-------------------------End Function to Insert User--------------------*/
/*-----------------------Function for Errors------------------------------*/
function errorCheck($formNames, $nameSplit, $fullname){
$isValid = false;
if (empty($fullname)) {
$_SESSION['error'][] = '<br><br> Error: Name Missing Here: '.$fullname.'<br><br>';
} elseif (empty($nameSplit[0])) {
$_SESSION['error'][] = '<br><br> Error: First Name Missing Here: '.$fullname.'<br><br>';
} elseif (empty($nameSplit[1])) {
$_SESSION['error'][] = '<br><br> Error: Last Name Missing Here: '.$fullname.'<br><br>';
} elseif (preg_match('/[^A-Za-z, ]/', $fullname)) {
$_SESSION['error'][] = '<br><br> Error: Illegal Character Found in: '.$fullname.'<br><br>';
} else {
$isValid = true;
}
return $isValid;
}
//errorCheck function tests for errors in names and stops them from being entered in the
//database if there are errors in the name. Allows good names to go through
/*-----------------------------End Function for Errors---------------------*/
/*--------------------------Function for Redirect--------------------------*/
function redirect($url){
$string = '<script type="text/javascript">';
$string .= 'window.location = "' .$url. '"';
$string .= '</script>';
echo $string;
}
//redirect function uses a javascript script to redirect user because headers have already been sent.
/*-----------------------------End Function for Redirect-----------------------*/
// Connect to database
I connect to the database here//
// Initialize empty error array
$_SESSION['error'] = array();
foreach ($fullnames as $fullname) {
$nameSplit = explode(" ", $fullname);
//I open the database here
//opens the database
if (errorCheck($formNames, $nameSplit, $fullname)) {
$firstName = $nameSplit[0];//sets first part of name to first name
$lastName = $nameSplit[1];//sets second part of name to last name
$emailUser = $nameSplit[0].$email;//sets first part and adds email extension
newUser($firstName,$lastName,$emailUser,$active,$conn);//do this BELOW only for names that have no errors
}//ends if of errorCheck
}//ends fullnames foreach
if (count($_SESSION['error']) == 0) {
redirect('viewAll.php');
} else {
redirect('form.php');
}
/*Redirects to viewAll page only once and as long as no errors have been found*/
Your
class User {
public $nameStore, $fName, $lName, $email;
public function __construct ($fName, $lName, $email) {
$this->$fN = $fName;
$this->$lN = $lName;
$this->$eN = $email;
}
I would break this up into more specific parts such as GET and SET for each value you are trying to store in the Class:
class User {
private $fName, $lName, $email;
public function set_firstname($fname){
$this->fName = $fname;
}
public function set_surname($lName){
$this->lName = $lName;
}
public function set_email($email){
$this->email = $email;
}
public function get_email(){
return $this->email;
}
public function get_fname(){
return $this->fName;
}
public function get_surname(){
return $this->lName;
}
Then when you create the class, you can add and return each value individually, rather than forcing yourself to do them all at once. This is more flexible. But you can also add the values at the creation of the class as well if you wish, using the __construct similar to what you had already:
public function __construct ($fName = null, $lName = null, $email = null) {
if(!empty($fName)){
$this->set_firstname($fName);
}
if(!empty($lName)){
$this->set_surname($lName);
}
if(filter_var($email, FILTER_VALIDATE_EMAIL) !== false){
$this->set_email($email);
}
}
What this does is for each non-empty value it runs the corresponding SET method. Also checking that the email value is valid before saving it. If no values are passed to the class then it doesn't save anything.
Your setting up of the class is incorrect, firstly you need to include the class file into the working PHP so at the top of your page add:
include "path/to/users.class.php";
And then initiate the class correctly:
$userClassInstance = new User($firstName,$lastName,$emailUser);
When the above line runs, you will then have a User object containing three variables referenced as $userClassInstance. you can do var_dump($userClassInstance);
Be careful as your code has newUser as one line and also has an incorrect number of variables in the construct statement. Generally all the functions in a page should be placed inside an appropriate class, so all your string management functions such as errorCheck() could be put into the Users class to check the values given before assigning them to the variables in the class.
Finally, to view the stored variables you would then do:
print $userClassInstance->get_fname(); //will outout the value of the class $fName
I'm currently a beginner developer and have just started my first big project whilst I have spare time, What I'm trying to do is basically write variables to a html/tpl document, Which I have currently got working, Here is my code:
private function index(){
$username = 'MyUsername';
$onlineTime = 'MyOnlineTime';
$this->setParams('Username', $username); // $username Will be replaced by database queried results once completed.
}
And here is the setParams function.
function setParams($item1, $item2){
ob_start();
$theme = 'default';
include_once T . '/'.$theme.'/index.php'; // T . is defined at the beginning of the document.
if ((($html = ob_get_clean()) !== false) && (ob_start() === true))
{
echo preg_replace('~{(['.$item1.']*)}~i', ''.$item2.'', $html, 1);
}
}
And here is the coding inside the html/tpl document.
{username} has been online for {onlineTime} Hours
This is probably a very simple code for some of you but as this is my first attempt this is all I can do.
What I would like to do is have it so you can setParams as many times as you want without changing the $variable names like so:
private function index(){
$username = 'MyUsername';
$onlineTime = 'MyOnlineTime';
$this->setParams('Username',$username);
$this->setParams('OnlineTime', $onlineTime);
}
whilst keeping the setParams($item1, $item2)
But as you can imagine this just cuts the code completely. Does anyone know a solution to this problem? I've been searching all day without any real luck.
Thanks In Advance,
Ralph
I think what you need is a class with a static method;
<?php
class Params {
public static $params = array();
public static function setParam($key, $value) {
self::$params[$key] = $value;
}
public static function getParam($key) {
if (isset(self::$params[$key])) {
return self::$params[$key];
}
}
}
// Usage
// Set Username
Params::setParam("username", "JohnDoe");
Params::setParam("password", "12345");
echo Params::getParam("username");
echo Params::getParam("password");
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
<?php
class ann {
public function __construct($context, $orgs_id, $created_at) {
$this->context = $context;
$this->orgs_id = $orgs_id;
$this->created_at = $created_at;
}
function create(){
$createann = mysql_query("INSERT INTO anns(context,
orgs_id, created_at)
VALUES('$this->context',
$this->orgs_id, '$this->created_at'");
if($createann) echo "Duyuru Başarıyla Eklendi"; else echo "Duyuru
Eklenemedi";
}
function read($id){
$readann = mysql_query("SELECT * FROM anns WHERE id = $id");
$context = mysql_result($readann,0, "context");
$orgs_id = mysql_result($readann,0, "orgs_id");
$created_at = mysql_result($readann,0,
"created_at");
$ann = new ann($context, $orgs_id, $created_at);
return $ann;
}
function update($id, $context){
$updateann = mysql_query("UPDATE anns SET context =
'$context' WHERE id = $id");
if($updateann) echo "Update success"; else echo
"Update failed";
}
function delete($id){
$deleteann = mysql_query("DELETE FROM anns WHERE id
= $id");
if($deleteann) echo "Delete success"; else echo "Delete not success";
}
//crud fonksiyonlari burda bitiyor
}
?>
There is something wrong with our logic here but we are very new to php. We tried to create rails like models, but it think something with our class-object notation is wrong. So the code did not work. We cannot even create any object with it.
Thank you guys
context, orgs_id and created_at must be should be first declared either as public, private or protected before you use them.
In your create method, you don't filter user input. This may cause to your application SQL injection, you have to you always filter user input. Use either mysql_real_escape_string or prepared statment by PDO.
You may check this tutorial.
two things (which maybe only apply to your codesample here):
In your sample, you dont close your
Class, because the last "}" is
commented out.
You never opened a connection to your database, so the query would fail.
a few observations:
declaring the attributes in the constructor is possible, but it's not elegant. I'd rather do:
class ann {
private $context;
private $orgs_id;
the "->" operator won't work inside a string. You'll need to concatenate the query:
"INSERT INTO anns(context,orgs_id, created_at) VALUES('".$this->context."',".$this->orgs_id".", '".$this->created_at."'"
but be careful on sql injection
The rest should be fine! Good Luck.