How to write OOP in PHP - php

Can anybody help is this code can be called OOP design. How to Create a class in PHP which shows that is OOP design.
I'm trying to create a simple example class before I start implementing the following into my projects and I would like to know if there is something I could/should improve. I will be very glad if someone gives me some input of how am I doing now.
This is simple class which is sorting associative array columns. Can anybody write simple class regarding sorting associative array. It can be anything just should follow OOP design pattern.
Thanks in advance for help.
<?php
interface PassangerBoardingInterface
{
public function ArrageBoardingTicket(string $from, string $to, string $instruction) ;
public function GetEmbededHTMlTemplate ( $template, $regrex);
}
class PassengerBoardingSorterOOP implements PassangerBoardingInterface
{
// Boarding list
public $boardingList;
// Journey start message
public $startMessage;
// End message
public $endMessage;
// Get Template
public $getTemplate;
public function __construct(array $boardingList )
{
$this->boardingList = $boardingList;
}
public function ArrageBoardingTicket(string $from, string $to , string $instruction)
{
foreach($this->boardingList as $boardingList)
{
usort($this->boardingList, function ($a, $b) use ($to, $from){
return ($a[$to] === $b[$from] ) ? 0 : 1;
});
}
// Defining index
$i = 0;
$coutboarding = count($this->boardingList);
// Appending data to start index
$this->boardingList[0][$instruction] = $this->startMessage;
// Appending string to end column
$this->boardingList[$coutboarding - 1 ][$instruction] = $this->endMessage;
}
public function GetEmbededHTMlTemplate($template, $regrex)
{
$result = '';
/* Loop each data */
for($j = 0; $j < count($this->boardingList); $j++)
{
/* Get the template */
$output = $template;
/* where indexs matches in template */
foreach($this->boardingList[$j] as $key => $value)
{
$reg = str_replace('(.*?)',$key, $regrex);
/* Check with regular expression */
if(preg_match($reg,$template))
{
/* Replace with */
$output = preg_replace($reg,$value,$output);
}
}
$result .= $output;
}
$this->getTemplate = $result;
}
}
==============================
I am using below structure
//My array of information
$cards = [
[
"from" => "Barcelona",
"to" => "New York",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Flight' ,
'transportno' => 'B33',
'seatno' => 'Y15'
],
[
"from"=> "Barcelona",
"to" => "Gerona",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Bus' ,
'transportno' => 'M31, M32, M33','seatno' => 'Any'
], // 1
[
"from" => "Madrid",
"to" => "Barcelona",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Bus' ,
'transportno' => 'M31, M32, M33',
'seatno' => 'Any'
],
["from" => "New York",
"to" => "Stockholm",
"instruction" => "",
'time' => '2018-02-02 20:05', 'transport' => 'Flight' ,
'transportno' => 'M31, M32, M33','seatno' => 'Any'
], // 0
[
"from" => "Gerona",
"to" => "Barcelona",
"instruction" => "",
'time' => '2018-02-02 20:05',
'transport' => 'Bus' ,
'transportno' => 'M31, M32, M33',
'seatno' => 'Any'
], // 2
];
$obj = new PassengerBoardingSorterOOP($cards);
$obj->startMessage = 'Your journey start from here ';
// End message
$obj->endMessage = 'Your journey ends here';
$obj->ArrageBoardingTicket('from', 'to', 'instruction');
// Form a template so you can get template list
$template = '<li class="list-group-item">{{&instruction}}Take {{&transport}} From {{&from}} To {{&to}},
{{&transport}} No {{&transportno}}, {{&transport}} Departure Date {{&time}}. Seat Number {{&seatno}}.</li>';
$regrex = '/{{&(.*?)}}/';
$obj->GetEmbededHTMlTemplate($template, $regrex);
echo $obj->getTemplate;

The code you provided violates a few rules for OOP hence can not be considered as such.
It is a representation of objects that interact with each other in order to create your program, instead of scripts that call each other (hope this makes sense).
You have to start thinking how you can represent your problem into objects.
Let's say you have a car to represent in PHP.
You will start to think you can have:
A car class;
A Wheel class (you car can have 4 wheels);
Then you think "But a car is a vehicle and, for example, a bike only have 2 wheels" and then you create your "Vehicle class";
Then you enhance your Car Class and extend it from Vehicle;
Now, each Vehicle have tyres, but you do not specify how many, that is for each type to decide, not the parent class that "abstracts them".
And so on. You quickly see here that to tackle this problem (and we are on the very start) you already have;
Abstract Class Vehicle;
Car Class (that extends Vehicle);
Bike Class (that extends Vehicle);
Wheel Class (That can be added to Vehicle types -> Your Car and your Bike);
When you want to move to OOP there are a few rules you need to consider:
Encapsulation;
Polymorphism;
Open/Close implementation;
Inheritance;
Open Recursion;
You want to restrict your Class objects to only understand "what they are". So a Car does not hold logic for a Bike and vice-versa.
1. Encapsulation -> This prevents external code from manipulating or "holding concern" to what happens on the target class internally;
It refers to the use of private and protected methods;
How the Car class behaves and what it triggers to work in whatever logic you decide a Car should work, is completely hidden for the other classes like Bike. They do not need to know what clicks on each other!
2. Polymorphism -> For example, the number of tyres, or even the Tyre types for each Vehicle, it will be different. Therefore you can declare the Vehicle can addTyres BUT each Vehicle type (like Car and Bike) will implement how to addTyres differently (You can limit bikes to have 2 tyres only whilst cars have 4);
3. Open/Close implementation -> When you create classes you should think on them as they should be OPEN for extension BUT CLOSED for modification. This means you can extend their behaviour BUT you should never have to go back and change your object behaviour;
4. Inheritance -> Allows your Classes to implement "hierarchy". Think of it as "parent" and "children". What came "First" and what came "after". Like a Shape is a parent of a Circle. If you announcing your argument, Shape is very abstract as it represents anything from Squares, Triangles, Circles, etc. But if you refer to a Square you know about it in more detail, like vertices, angles, etc! Shapes represent that but refine nothing towards what type, just what structure should be but not "how it will be";
5. Open Recursion -> Classes are able to call each other and even other classes. That is how they interact. Hell they can even have other objects as part of their attributes;
In a very resumed source this is a very BRIEF introduction to OOP!
Once you get the hang of it it is amazing and you will have such a powerful logical code and so organised I swear to you, you will not go back :3
But yeah, this is not enough to even begin telling you OOP! :x Just think of it this way, it is a walk, not a destination :D
PS: A great way to start getting used to OOP is also starting to look at Design Patterns

Related

CakePHP 3.8 belongsTo does not persist changes to new association when association is changed

I am currently using CakePHP to serve a crud based api for some ticketing logic I wrote. I am running into an issue where I am attempting to change a belongsTo association and data within the new association and it is not persisting.
The controller doing the persisting looks like this:
<?php
class TasksController extends Cake\Controller\Controller
{
public function initialize()
{
parent::initialize();
}
public function edit(array $ids): void
{
$validationErrors = [];
$tasks = $this->Tasks->find('all')
->contain($this->setAssociations($query))
->where([$this->Model->getAlias().'.id IN' => $ids]);
foreach ($tasks as $id => $task) {
$this->Tasks->patchEntity($task, $this->request->getQuery()[$id], [
'associated' => ['Asset']
]);
}
if ($this->Tasks->saveMany($tasks)) {
$this->response = $this->response->withStatus(200);
} else {
// Handle errors
}
// Render json output for success / errors
$this->set($this->createViewVars(['entities' => $tasks], $validationErrors));
}
}
The association for an asset in the task table looks like this:
<?php
class TasksTable extends Cake\ORM\Table
{
public function initialize(array $config)
{
$this->belongsTo('Asset', [
'className' => 'Assets',
'foreignKey' => 'asset_id',
'joinType' => 'LEFT'
]);
}
}
These build rules are attached to the asset table:
<?php
class AssetsTable extends Cake\ORM\Table
{
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->isUnique(['number', 'group_id'], 'The Number you selected is in use'));
$rules->add($rules->isUnique(['name', 'group_id'], 'The Name you selected is in use'));
}
}
The body of the request I am sending looks like this:
{
"421933": {
"description": "This task was edited by the api!",
"asset": {
"id": "138499",
"description": "This asset was edited by they api!",
"name": "105",
"number": "6"
}
}
}
Basically the name 105 and number 6 are being flagged as not being unique, because they are already set to those values on asset 138499. The query is is instead trying to edit name 105 and number 6 into the Asset entity that is presently associated with the Task entity (645163), which is triggering the isUnquie build rules to fail.
You can see this by printing the $tasks before the saveMany call in the above controller:
Array
(
[0] => App\Model\Entity\Task Object
(
[id] => 421933
[description] => This task was edited by the api!
.....
[asset] => App\Model\Entity\Asset Object
(
[id] => 645163
[name] => 105
[description] => This asset was edited by they api!
[number] => 6
....
)
)
)
It seems like this editing Asset 138499 as an association of Task 421933 should work as it is appears to be possible to save belongsTo associations in this fashion in the CakePHP docs, as documented here:
<?php
$data = [
'title' => 'First Post',
'user' => [
'id' => 1,
'username' => 'mark'
]
];
$articles = TableRegistry::getTableLocator()->get('Articles');
$article = $articles->newEntity($data, [
'associated' => ['Users']
]);
$articles->save($article);
Is it possible to associate a belongsTo association and edit it in the same transaction? If so how should my request or code be structured differently?
Thanks!
As hinted in the comments, you're running into a limitation of the marshaller there, for which there is no overly startightforward workaround yet (you might want to open an issue over at GitHub, maybe someone want's to take up the task of adding support for this type of marshalling/saving).
The example in the book isn't really the best to go by here, in fact I'd personally remove/change it, it would only work when mass assigning the primary key is allowed (which by default for baked entities it isn't), and it would also only work when creating new records, ie when the entity that is to be saved is marked as "new". However doing this would introduce all sorts of quirks, like validation rules would see the data as "create" (new), application rules would see the entity as "update" (not new), but not receive any of the existing/original data, same for other saving stage callbacks, which would mess up stuff like auditing or the like, basically anything where you need the new data and the original data.
Long story short, even if you could get it somewhat working like with that example, you'd just introduce other problems further down the road, so better forget about that for now. The only workaround-ish thing that comes to my mind right now, would be comparing the given asset primary key, loading the existing asset entity manually, and replacing the already loaded one before patching in your data, something along the lines of this:
foreach ($tasks as $task) {
$data = $this->request->getData($task->id);
$assetId = (int)\Cake\Utility\Hash::get($data, 'asset.id');
if ($task->asset->id !== $assetId) {
$task->asset = $this->Tasks->Asset->get($assetId);
}
$this->Tasks->patchEntity($task, $data, [
'associated' => ['Asset']
]);
}
Note that I've changed your getQuery() usage to getData(), as it seemed unlikely that you're passing that data via the query string.

Eloquent first() and loops

I'm new to Eloquent and I'm having trouble when using it with loops.
Imagine I have a table in my db called ‘Jobs’ with:
Id – 1; value: engineer
Id – 2 ; value: doctor
Id – 3 ; value: nurse
I'd also have a JobTranslations table with each job translations. Then, I have this piece of code:
$jobTranslations = [];
$lang = $request->input('lang') ?? 'pt';
foreach(Job::all() as $job) {
$job = $job->with(['jobtranslations' => function($query) use ($lang) {
$query->where('languageCode', $lang);
}])->firstOrFail();
$jobTranslations[] = $job;
}
return ['translatedJobs' => $jobTranslations];
When I run this code, I’m expecting to get an array (jobTranslations) with those 3 jobs in it and each job’s translation.
However, I get instead an array with three items, but they’re all the same:
array([‘id’ => 1, ‘value’ => ‘engineer’, ‘jobtranslations’ => [another array]],
[‘id’ => 1, ‘value’ => ‘engineer’, ‘jobtranslations’ => [another array]],
[‘id’ => 1, ‘value’ => ‘engineer’, ‘jobtranslations’ => [another array]]
);
I’ve realized this is happening due to ‘firstOrFail’ but I don’t understand why. $job->firstOrFail() doesn’t make sense to me because it’s only one job (the current one in the loop) but I don’t want to use get() either.
In C#, using EF, I’d do something like this (no need to call get or first):
foreach(var job in jobs) {
translatedJobs.Add(job.Include(j => j.jobtranslations.FirstOrDefault(jt => jt.languageCode == lang)));
}
return translatedJobs;
Thanks in advance!
I assum what you ar looking for ist this:
$lang = $request->input('lang', 'pt');
$jobTranslations = Job::with([
'jobtranslations' => function ($query) use($lang)
{
$query->where('languageCode', $lang);
}
])->get()->toArray();
return ['translatedJobs' => $jobTranslations];
There is no need to do a Foreach Loop if you setup your Model correct.

How to show dirty values when debugging entity

I need use _get so I just did it at User entity just for test:
protected function _getName($name)
{
return $name . ' - FOOBAR';
}
So in the view I did Debug($user), and heres the result:
'properties' => [
'id' => (int) 32,
'name' => 'Daniel Pedro', //<- Clean Value
'email' => 'daniel#gmail.com',
],
'dirty' => [],
'original' => [],
'virtual' => [],
'errors' => [],
As you can notice the property name is with the original value Daniel Pedro, so I thought I did something wrong at _getName but when I look at the input at form the value was Daniel Pedro - FOOBAR.
My question is, how can I show the mutated values at Debug?
Debug the values separately
The most simple way to check the properties with their possible muatated values, is by extracting the visible properties, something like
debug($entity->extract($entity->visibleProperties()));
This won't include the ones that have been defined as "hidden" in the $_hidden property, if you need them too, then you'll have to explicitly include them
debug($entity->extract(array_merge($entity->visibleProperties(), $entity->hidden())));
Extend the debug info
If you'd wanted to somehow include this in the debug output of entities in general, then you'll have to overwrite the EntityTrait::__debugInfo() method and add the mutated properties in there.
Simple example, in your entity class (you can create a base entity class that all your entites extend so that you have this functionality in all entities):
public function __debugInfo()
{
$info = parent::__debugInfo();
$info['propertiesIncludingPossiblyMutatedValues'] =
$this->extract(array_keys($this->_properties));
return $info;
}
Or if you'd wanted to see only the ones that have really been mutated:
public function __debugInfo()
{
$info = parent::__debugInfo();
$info['mutated'] = array_diff(
$this->extract(array_keys($this->_properties)),
$this->_properties
);
return $info;
}
This should give you a hint of how things work.

PHP Parsedown get all headings

I use Parsedown for transforming my Markdown into HTML like:
$pd->text('# My First heading');
In want automatically use the first site heading (h1) as an entry for a custom menu. I already had a look into the source, but couldn't figure out a good way to get all headings.
Perhaps there is something in elements function. Does anyone has an idea to get all or at least the first heading.
#tobias-redmann
couldn't figure out a good way to get all headings
I assume you already solved this problem. But since I had the same problem and came up with the answer, I would like to share some info for those who will confront with the same problem.
TL;DR
Getting ToC (Table of Contents) w/ Parsedown.
<?php
include('Parsedown.php');
class Extension extends Parsedown
{
public $array_line = array();
// Override
protected function blockHeader($Line)
{
// Parse $Line to parent class
$Block = Parsedown::blockHeader($Line);
// Set headings
if(isset($Block['element']['name'])){
$Level = (integer) trim($Block['element']['name'],'h');
$this->array_line[] = [
'text' => $Block['element']['name'],
'level' => $Level,
];
}
return $Block;
}
}
$text = file_get_contents('YourMarkdownFile.md');
$Parsedown = new Extension();
$string_body = $Parsedown->text($text);
$array_ToC = $Parsedown->array_line;
print_r($array_ToC);
//echo $string_body;
TS;DR (Details)
First of all, as #emanuil-rusev said, overriding the blockHeader method, you can get all the headings from the markdown text.
More specifically, when you call text($text) method, it parses the markdown strings given.
While parsing, the blockHeader($Line) method will be called every line with an array argument such as below.
$Line => [
"body" => "# SampleHead",
"indent" => 0,
"text" => "# SampleHead",
]
The original (parent) class' blockHeader($Line) method converts $Line into the following array($Block).
$Block => [
"element" => [
"name" => "h1",
"text" => "# SampleHead",
"handler" => "line",
],
]
So, the easiest way to get all the headings is to store these each time into an array.
Full script (extension) to get table of contents
See my repo:
https://github.com/KEINOS/parsedown-extension_table-of-contents
Author of Parsedown here.
You should be able to achieve this by creating an extension that overrides the blockHeader method. Basically, the overriding method should modify the result of the parent method.
Have a look at the Parsedown wiki for more info.

Can I get an example please?

$starcraft = array(
"drone" => array( "cost" => "6_0-",
"gas" => "192",
"minerals" => "33",
"attack" => "123",
)
"zealot" => array( "cost" => "5_0-",
"gas" => "112",
"minerals" => "21",
"attack" => "321",
)
)
I'm playing with oop and I want to display the information in this array using a class, but I don't know how to construct the class to display it.
This is what I have so far, and I don't know where to go from here. Am I supposed to use setters and getters?
class gamesInfo($game) {
$unitname;
$cost;
$gas;
$minerals;
$attack;
}
You're actually pretty close so far.
In OOP, each object usually represents a discrete concept. Therefore, a better name for your class would be Unit because it represents a unit in the game. Example:
class Unit {
var $name;
var $cost;
var $gas;
var $minerals;
var $attack;
function setName($name) {
$this->name = $name;
}
function getName() {
return $this->name;
}
}
Note that this example has the appropriate instance variables (name, cost, etc) and a getter/setter pair for the name variable. You'd want to add more getter/setter pairs for the other instance variables.
Once you have all your getter/setters, you can instantiate a Unit by doing this:
$zealot = new Unit();
$zealot->setName("Zealot");
$zealot->setAttack(321);
... etc.
You'll also want to learn about constructors, so that you can instantiate a Unit this way:
$zealot = new Unit("Zealot");
$zealot->setAttack(321);
You can see that a constructor would give you a bit of a shortcut there by letting you set the unit name at the same time you instantiate the Unit class.
So to print a Unit object, you'd do something like:
echo $zealot->getName();
Edit: Like zerkms said, OOP is complex, and what I described here is basically programming with classes and objects. This is just the very beginning of OOP.
Why did not you start with http://ru.php.net/manual/en/language.oop5.basic.php?

Categories