I'm trying to use OOP to create an effective class that loops through a JSON file and echo's out certain items. Inside of the class I need to use methods like "getName()" etc. The goal is to use a class as much as possible. Is there a better way of doing this?
$file= file_get_contents("swapi.json");
$data = json_decode($file);
$content = $data->results;
foreach($content as $item){
$name = $item->name;
$height = $item->height;
$person = new Character($name,$height);
echo $person->getName(),$person->getHeight();
}
class Character{
public $name;
public $height;
public static $number_of_char = 0;
function __construct($name,$height){
$this->name = $name;
$this->height = $height;
Character::$number_of_char++;
}
function getName(){
return "Character number ".Character::$number_of_char.":".'</br>'.
'<li>'."Namn: ".$this->name.'</li>';
}
function getHeight(){
return '<li>'."height: ".$this->height."cm".'</li>'.'</br>';
}
}
JSON
{
"count": 87,
"next": "https://swapi.co/api/people/?page=2",
"previous": null,
"results": [
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
This is as as compressed as I've managed to get it.
Echos out 10 different objects containing for example:
"Character number 1:"
- Name: Obi-Wan Kenobi
- height: 182cm
Is this good practice? Iv'e tried using loops inside the class to get the data but haven't been able to make it work. (The 'foreach($content..' is in a separate file)
Related
<?php
include '../classes/productsContr.cls.php';
$sku = $_POST['sku'];
$name = $_POST['name'];
$price = $_POST['price'];
$type = $_POST['type'];
$size = $_POST['size'];
$height = $_POST['height'];
$width = $_POST['width'];
$lenght = $_POST['length'];
$weight = $_POST['weight'];
$ob = new productsContr($sku,$name,$price,$type,$size,$height,$width,$lenght,$weight);
this is the addProducts.inc.php file. it takes the post request parameters and sends them to the controller file
<?php
include 'products.cls.php';
class productsContr extends Products {
private $sku;
private $name;
private $price;
private $type;
private $size;
private $height;
private $width;
private $length;
private $weight;
public function __construct($sku, $name, $price, $type, $size, $height, $width, $length, $weight) {
$this->sku = $sku;
$this->name = $name;
$this->price = $price;
$this->type = $type;
$this->size = $size;
$this->height = $height;
$this->width = $width;
$this->length = $length;
$this->weight = $weight;
}
private static $specialAttributes = [
"DVD-Disk" => [$this->size],
"Furniture" => [
$this->height,
$this->width,
$this->length,
],
"Book" => [$this->weight],
];
$ob = new Products();
$ob->addProduct(
$sku:$this->sku,
$name:$this->name,
$price:$this->price,
$size:$this->size,
$height:$this->height,
$width:$this->width,
$length:$this->length,
$weight:$this->weight
);
}
this is the productsContr.cls.php file and it should tell the model file how to add the products to the database.
<?php
include 'dbh.cls.php';
class Products extends Dbh{
public function addProduct(
$sku = '',
$name = '',
$price = 0,
$size = 0,
$height = 0,
$width = 0,
$length = 0,
$weight = 0
) {
$sql = "INSERT INTO products (product_sku, product_name, product_price, product_size, product_height, product_width, product_length, product_weight) ($sku, $name, $price, $size, $height, $width, $length, $weight);";
}
}
this is the products.cls.php model file.
with each product I want to send the sku, the name, the price, and the special attributes for each type as in the associative array $specialAtrributes, but without using if or switch statements
(this is a test task)
I thought this would work with the associative array but I am stuck right now. any solutions?
First things first
First of all your code example seems to be wide open for sql injection. Please fix this first. There are some rules when it comes to software developments. One of the most important patterns is seperate things as most as you can. Your code tries to mix up different things. That 's always not a good approach.
Seperation of concerns
Think of the terms you use. When we are speaking of products we as humans know, that products can be a furniture, a book or what so ever. Taking a deeper dive we will recognize, that a book has different attributes than a furniture. When thinking of seperation of concerns we have to seperate this two things, because they have different attributes.
Data modeling
As you can see in your own code you will always run into trouble when trying to do several things at once. First think of all attributes that all products have in common. As you already found out sku, name and price are attributes that all products have in common. Let us create an abstraction of that.
<?php
declare(strict_types=1);
namespace Marcel\Model;
abstract class Product
{
public function __construct(
protected string $sku = '',
protected string $name = '',
protected float $price = 0,
) {}
public function toArray(): array
{
return get_object_vars($this);
}
}
The abstract class above takes all properties every product has in common. From now on you 're able to inherit from that class and create all the products you want to offer.
<?php
declare(strict_types=1);
namespace Marcel\Model;
class Furniture extends Product
{
public function __construct(
protected string $sku = '',
protected string $name = '',
protected float $price = 0,
protected float $height = 0,
protected float $width = 0,
protected float $length = 0
) {}
}
class Book extends Product
{
public function __construct(
protected string $sku = '',
protected string $name = '',
protected float $price = 0,
protected float $weight = 0
) {}
}
Seems to be a bit of work. But this effort will pay off very easily later. Because with this exactly defined product models you 're able to do some fancy things like hydration. But let us take a look at your controller first.
The controller
As you might already know your controller handles all your incoming data. Because you don't know what data you are receiving, you have to be a bit more dynamic at this point. As long as you know the type of your product, our project is very simple.
<?php
declare(strict_types=1);
namespace Marcel\Controller;
use InvalidArgumentException;
use ReflectionClass;
class ProductController
{
public function addAction(): void
{
/*
[
'sku' => 'abc-123',
'name' => 'harry potter',
'price' => '99.99',
'type' => 'book',
'size' => '',
'height' => ''
'width' => '',
'length' => '',
'weight' => '350',
]
*/
if ($_POST) {
if (! isset($_POST['type'])) {
throw new InvalidArgumentException('No product type');
}
$productFqcn = '\\Marcel\\Model\\' . ucfirst($_POST['type']);
if (! class_exists($productFqcn) {
throw new InvalidArgumentException('Invalid product type');
}
// this will throw type errors because you have to sanitize your
// post data before hydration. Take care of floats and strings.
$product = new $productFqcn();
$reflector = new ReflectionClass($product);
$properties = $reflector->getProperties();
foreach ($properties as $property) {
if (isset($_POST[$property->getName()])) {
$property->setAccessible(true);
$property->setValue($product, $_POST[$property->getName()]);
}
}
// at this point $product contains all relevant properties
}
}
}
The controller above uses reflection to find out which properties your product has. A book has no height property so it won 't be hydrated. At the end the book data object contains all the needed properties.
Hydration is a common approach when it comes to data handling. As statet out in the controller you have to sanitize your post data before hydration. Just filter and validate your incoming data that it fits for the declared types in the data models.
Repositories for database handling
The same abstraction level goes for repositories, that can store the received data into the database. Because every product has different properties, you can seperate into different product database tables. If you want to store every product in just one single table watch out for columns with null as default value.
The following approach assumes one single product table.
<?php
declare(strict_types=1);
namespace Marcel\Repository;
use Marcel\Model\Product;
class ProductRepository
{
public function add(Product $product): void
{
// find your which datafields to store
$data = $product->toArray();
$sql = 'INSERT INTO products (%s) VALUES (:%s)';
$sql = sprintf(
$sql,
implode(', ', array_keys($data)),
implode(', :', array_values($data))
);
// sql prepared statement here and so on
}
}
As you can see the repository just takes a single parameter $product, which must be an instance of our product data model. Type hints lead to type safety. From now on you 're able to take any product, extract its data and insert it into a database table dynamically. The example above hints to prepared statements. Another point you have an eye on.
Let us extend the controller from above.
<?php
declare(strict_types=1);
namespace Marcel\Controller;
use InvalidArgumentException;
use Marcel\Repository\ProductRepository;
use ReflectionClass;
class ProductController
{
public function addAction(): void
{
...
// code from above
// persist a new product in the database
$repository = new ProductRepository();
$repository->add($product);
}
}
Conclusion
It is important to think about how the data is structured before programming. Get a clear picture of what data is the same and what data is not. Abstract as much as possible, because you don't want to make unnecessary work for yourself. Always follow the separation of concerns strategy. This will make your life much easier in the future.
As you can see from the code shown, it is not really easy to abstract so far that you can store all products with one controller. But it is possible. However, you will not get around some important things like input filters and prepared statements. These are elementarily important for the security of your application.
But hey ... no if or switch conditions shown in the code above, to seperate the different products. ;)
I'm trying to edit an class with an DB driven result.
I use ColorInterpreter by Our Code World, which is a PHP port of the JavaScript library NTC JS.
In this class is a public variable which is a array of hex/colorname pairs. This is hardcoded into the class. I want to make this dynamic with the use of an DB output.
I'm still struggeling with classes so I can't get my head around this.
colornames.inc.php:
Outputs something like this:
Array
(
[b0bf1a] => Acid Green
[7cb9e8] => Aero
[c9ffe5] => Aero Blue
[b284be] => African Violet
[00308f] => Air Force Blue (USAF)
[72a0c1] => Air superiority Blue
...
}
ColorInterpreter.php:
class ColorInterpreter
{
public function __construct()
{
$color = null;
$rgb = null;
$hsl = null;
$name = null;
for($i = 0; $i < count($this->names); $i++)
{
$color = "#".$this->names[$i][0];
$rgb = $this->rgb($color);
$hsl = $this->hsl($color);
array_push
(
$this->names[$i],
$rgb[0],
$rgb[1],
$rgb[2],
$hsl[0],
$hsl[1],
$hsl[2]
);
}
}
public function name($color)
{
...
}
// adopted from: Farbtastic 1.2
// http://acko.net/dev/farbtastic
public function hsl($color)
{
...
}
// adopted from: Farbtastic 1.2
// http://acko.net/dev/farbtastic
public function rgb($color)
{
...
}
public function color($name)
{
...
}
// Below is the part I need to replace with the output given from colornames.inc.php
public $names = array(
// Pink colors
array("FFC0CB", "Pink"),
array("FFB6C1", "Light Pink"),
array("FF69B4", "Hot Pink"),
array("FF1493", "Deep Pink"),
array("DB7093", "Pale Violet Red"),
array("C71585", "Medium Violet Red"),
array("E0115F", "Ruby"),
array("FF6FFF", "Ultra"),
...
);
}
I don't know how, if or where I could/should include the colornames.inc.php
And I don't know where to correctly declare the needed variables. Obviously, $this->names represent the hardcoded array, but how has this to be changed to reflect the DB output I have?
I'm completely lost here.
Your best option is to create a function that returns the values, and then pass that as the constructor when you create the class. e.g.
include 'file_with_function.php';
include 'file_with_class.php';
$initialColorValues = my_get_color_values();
$newObject = new ColorObject($initialColorValues);
Now you can process that array in the constructor, and you have an object that is seeded with the correct color values
class XYZ {
public $names;
public function __construct($names){
// ... do work
$this->names = $work;
}
}
I want to save REST API data with array to database, its work, all value save to the table, but i still get error when i want to view the result in json.
Here is my error
"message": "Argument 1 passed to App\\Transformers\\NewLoanOfferTransformer::transform() must be an instance of App\\Model\\AntiAttrition, array given, called in /home/insko23/testing.insko.my/vendor/league/fractal/src/Scope.php on line 338",
My Controller
public function new_loan_offer(Request $request, AntiAttrition $antiattrition)
{
$new = array_map(null, $request->mo_id, $request->id_clrt,$request->TaskID,$request->ACID,$request->CustIDNo);
foreach($new as $new) {
$request = new AntiAttrition;
$request->mo_id = $new[0];
$request->id_clrt = $new[1];
$request->TaskID = $new[2];
$request->ACID = $new[3];
$request->CustIDNo = $new[4];
$request->save();
}
$response = fractal()
->item($new)
->transformWith(new NewLoanOfferTransformer)
->toArray();
return response()->json($response,201);
}
My App\Transformer
<?php
namespace App\Transformers;
use App\Model\AntiAttrition;
use App\Model\SettlementInfo;
use League\Fractal\TransformerAbstract;
class NewLoanOfferTransformer extends TransformerAbstract
{
public function transform (AntiAttrition $antiattrition)
{
return[
'id' => $antiattrition->id,
'mo_id'=> $antiattrition->mo_id,
'assign_by'=> $antiattrition->assigned_by,
'id_clrt' => $antiattrition->id_clrt,
'TaskID'=> $antiattrition->TaskID,
'ACID'=> $antiattrition->ACID,
'CustIDNo'=> $antiattrition->CustIDNo
];
}
}
I want to show the result in json, like below
{
"data": [
{
"mo_id": "123",
"id_clrt": "10000000049",
"ACID": "123",
.....
},
{
"mo_id": "1234",
"id_clrt": "10000000045",
"ACID": "1235",
.....
},
{
"mo_id": "124",
"id_clrt": "10000000044",
"ACID": "1245",
.....
},
],
}
Please help me to solve this problem
In the foreach loop, avoid using same name for array and elements of the array you are iterating over, you can rename foreach($new as $new) to foreach($newArray as $new), or something meaningful with your code logic. Also rather than using $new[0] use $new['mo_id'] for referring to the key of the array
I have a foreach loop that is supposed to loop through JSON and return the appropriate ID of each video listed in JSON using the Youtube api.
Here is my code:
class Videos {
private $mVideoUrl;
function setVideoTitle($videoUrl){
$this->mVideoUrl= $videoUrl;
}
function getVideoTitle(){
return $this->mVideoUrl;
}
}
$jsonFile = file_get_contents($url);
$jfo = json_decode($jsonFile);
$items = $jfo->items;
$vidArray = array();
foreach ($items as $item){
if(!empty($item->id->videoId)){
$Videos = new Videos;
$Videos->setVideoUrl($item->id->videoId);
$id = $Videos->getVideoUrl();
array_push($vidArray, $id);
}
echo $vidArray[0];
}
Problem is, the array push is working correctly, but it is only adding the 1st ID in the list only for each loop iteration when i echo it. When I echo the $id variable, it prints all IDs just fine.
Ultimately i want to be able to create an object for each video, storing it's ID and other information.
I feel like this is a simple fix but i can't figure it out for the life of me.
I would appreciate any help!
Also if i am going about this all wrong, advice is appreciated as well!
Thanks!
I've played little bit with your code. I've modified your class. I've renamed plurar Videos to Video (singular).
Then I've added an attribute $id, because name of properties should be simple and represent the data we want to store in them.
Then I've added getter and setter for the $id property.
I don't know the $url, so I've just write simple JSON string instead. I tried to mimic the structure that you're using in your code.
Then I've added () to the end of new Video() to have proper constructor called.
And instead of pushing elements into array, I'm using proper $array[$index] = assignment.
Last thing, I've moved writing out the data out of the foreach-cycle. And I'm using var_export to get proper php code to play with if redirected to another file.
<?php
class Video
{
private $mVideoUrl;
private $id; // added id attribute
/**
* #return mixed
*/
public function getId() // added getter
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id) // added setter
{
$this->id = $id;
}
function setVideoTitle($videoUrl)
{
$this->mVideoUrl = $videoUrl;
}
function getVideoTitle()
{
return $this->mVideoUrl;
}
}
// ignored for now
// $jsonFile = file_get_contents($url);
$jsonFile = '{"items": [
{ "id": { "videoId": 1, "url": "http://www.youtube.com/1" } },
{ "id": { "videoId": 2, "url": "http://www.youtube.com/2" } },
{ "id": { "videoId": 3, "url": "http://www.youtube.com/3" } },
{ "id": { "videoId": 4, "url": "http://www.youtube.com/4" } },
{ "id": { "videoId": 5, "url": "http://www.youtube.com/5" } }
]
}';
$jfo = json_decode($jsonFile);
$items = $jfo->items;
$vidArray = array();
foreach ($items as $item)
{
if (!empty($item->id->videoId))
{
$Video = new Video(); // added brackets
$Video->setId($item->id->videoId); // changed to setId
$Video->setVideoTitle($item->id->url);
$id = $Video->getId();
$vidArray[$id] = $Video;
}
}
// write out all data
var_export($vidArray);
In your code your class Videos contains two functions
setVideoTitle(...),
getVideoTitle()
but in your foreach you have called $videos->getVideoUrl() , $videos->setVideoUrl(...)
what is this ???
I am trying to add 4 foreach into one. I know how to add 2 foreach into one like this :
foreach (array_combine($images, $covers) as $image => $cover) {
But i want to add more two foreach $titles as $title and $albums as $album.
I am not sure want like this :
foreach (array_combine($images, $covers) as $image => $cover) {
foreach (array_combine($titles, $albums) as $title => $album) {
echo "$image-$cover-$title-$album"
It show me duplicate of every output.I mean output is
demo-demo1-demo2-demo3demo-demo1-demo2-demo3
Need output only
demo-demo1-demo2-demo3
Put the for each statement in a function. Then create a loop that calls it.
public function loopMe($images, $covers)
{
foreach (array_combine($images, $covers) as $image => $cover) {
$this->loopMe($image,$cover);
}
}
It looks like your second for loop is being called multiple times per item in the first for loop. So you want to make sure that you are only calling the second for loop once per image cover. or set an index cap on the second for loop. For instance if you are tyring to map the first item in the first for loop to the first item in the second for loop you should use an index.
public function loopMe($images)
{
for ($i = 0; $i < count($images); $i++) {
echo $images[$i] . '-'. $title[$i] . '-' . $cover[$i] . '-'. $album[$i];
}
}
I think you are approaching this problem from the wrong angle. From what I can tell, you are trying to output the properties of something. I think what you want to do is approach this from an object-oriented approach by creating a class and using a method to output the contents of your object.
Something like this:
class MyAlbumThing {
protected $image;
protected $cover;
protected $title;
protected $album;
public __construct($image, $cover, $title, $album) {
$this->image = $image;
$this->cover = $cover;
$this->title = $title;
$this->album = $album;
}
public getImage() {
return $this->image;
}
public getCover() {
return $this->cover;
}
public getTitle() {
return $this->title;
}
public getAlbum() {
return $this->album;
}
public getString() {
return $this->image . '-' .
$this->cover . '-' .
$this->title . '-' .
$this->album;
}
}
Then, you can instantiate this class and print your properties:
MyAlbumThing album = new MyAlbumThing("demo", "demo1", "demo2", "demo3");
echo $album->getString();
Would output:
demo-demo1-demo2-demo3
Also, if you hav a lot of these things, then you would use a foreach, like so:
$myAlbumArray = new array();
$myAlbumArray[] = new MyAlbumThing("demo", "demo1", "demo2", "demo3");
$myAlbumArray[] = new MyAlbumThing("anotherdemo", "anotherdemo1", "anotherdemo2", "anotherdemo3");
$lengthOfArray = sizeof(myAlbumArray);
for ($i = 0; $i < $lengthOfArray; $i++) {
echo $myAlbumArray[$i]->getString();
}
Sorry for any errors in my syntax, I wrote that in the browser without the help of my IDE.
I highly recommend learning more about object-oriented PHP programming. I found this article especially helpful while I was learning: http://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762
EDIT:
Please mark this answer as correct if you did indeed find this helpful for your problem.