splitting large controller method into files - php

my code is far from DRY and it needs refactored but for the time being i need to split it into files- i have a 300 line controller method that does a lot of api work when called and i just want to put some code into another file so i can read it a bit better without going and re-writing it.
if($type == "like"){
$this->load->helper('posts/likes');
}else{
$this->load->helper('posts/pic');
}
i tried the above method but its treating it like a normal (would you believe it)-
basically i want to copy and paste the code into another file to clean it up slightly, but the new file should just be an extension of the current method. i dont what to to use php functions (require_once or similar).. can ci not do this?
to clarify
I had a very large controller method- and instead of having 400 lines of code in the one method i want to split it in two files and have the code reside in there. If the conditional passed one file would load into the controller instead of another-
answering my question on how to load files by posting about DRY class methods didnt help my situation- the code should be cleaned- refactored and sorted into a library, i know this but i need a temp fix
my answer
The fix was to to lump the code into a subfolder within the controller directory for my posts controller i put the two files in the posts subfolder then used require_once- which worked but i thought ci may have soemthing to load blocks of code.

General rule is to keep controller slim.
DRY is the thing you need to take seriously and get ride of it.
Usually I will prepare the controller to call a single model method,
and this model method WILL prepare, and return all the necessary data in correct format back to controller method
so, you can avoid result looping in controller
$result = $this->some_source->get_comments($limit=30);
// I don't do loop in controller
$this->load->view("display", $result);
class some_source extends xxx
{
function get_comments(...)
{
// get results
// loop
// do necessary massage / format
// and return
}
}

Related

php/oop/mvc order of operation with markdown files (no db)

Goal: understand whether this implemented order/logic of operation indeed belongs to the C part of MVC.
Situation: I have nearly finished a simple note-taking site with markdown files. No database is used except for authentication. The lack of database, however, makes it difficult to know if I am neglecting the M of MVC.
Because I did not want to have the .md extension as part of the pretty url, I am heavily relying on the PageController to settle the order of operation.
From the Controller, PageController inherits the constructed filesystem/Flysystem(fs), twig, and the "$app" that processes any of the three scenarios.
It first checks to see if a param.md exists and then whether param is a directory. If none of the above, then it's a new note. In each case, it uses a corresponding method set by the Application ($app) that processes and returns an array (title, breadcrumbs, content/directory listing, new template etc).
<?php
namespace App\Controller;
class PageController extends Controller {
public function Page($param){
$file=$param.'.md';
if ($this->fs->has($file)) {
$data=$this->app->setNote($file);
return $this->app->render('note.twig',$data)
}
elseif ($this->fs->has($param)) {
$data=$this->app->setFolder($param);
return $this->app->render('folder.twig',$data)
}
else {
$data=$this->app->setNew($param);
return $this->app->render('new.twig',$data)
}
}
}
Per "PHP the Right Way":
Controllers handle the request, process the data returned from models and load views to send in the response.
Although my code works and gets the job done, it does not appear to the be right way because the main App is doing the processing. I guess I could just move the Application.php in the Models folder, but would that make it adhere to "right way"? Should I use a Middleware before the PageController gets to work?
It may seem silly to ask about a code that just works, but my goal is to better understand/learn the current wisdom's ways when dealing with flat-files.
Regardless of whether you are 'database-less', the data is being stored / accessed in the .md files.
Access to them should be abstracted to a Model. You should create a File.find object + method, and/or a File.find_or_create. Then
$file = File.find_or_create($param);
$render_type = $file.type . '.twig';
return $this->app->render($render_type, $file.data);
Put all your if logic in the Model.

laravel 5 , passing values from multiple controllers

I am trying to send to a view some values from different controllers
Here is my code:
Route::get('/add_email','ListsController#index_add_email');
Route::get('/add_email','RepoController#repo_index_add_email');
I am trying to display on this page (add_email) values from those two functions. Of course how its now I am getting error because the second get will overwrite the first one. How do I mix those two "GET"?
Route::get('/add_email','ListsController#index_add_email');
Inside ListsController in index_add_email function add this row:
with(new RepoController())->repo_index_add_email();
btw, make sure to have a read PSR-1 && PSR-2 guides:
PSR-1: http://www.php-fig.org/psr/psr-1/
PSR-2: http://www.php-fig.org/psr/psr-2/
Laravel doesn't support to point same route ('add_email') to different controller.
if you need to do so, handle the logic inside the action you defined.
Route::get('/add_email','ListsController#index_add_email');
Route::get('/add_email','RepoController#repo_index_add_email');
You can set the route to redirect to one controller and then call the other controller from the controller called.
For e.g. in routes.php
Route::get('/add_email','ListsController#index_add_email');
and in ListsController
public function index_add_email(...)
{
// Other code
// Call RepoController function
return app('App/Http/Controllers/RepoController')->repo_index_add_email();
}
(Assuming that's the correct namespace for RepoController.php)
Although I think this is the easiest option, it does mess with code organization. You could try working with Events and Listeners, since it would make logical sense here where two different actions are performed during the same event but again, that's simply overkill.

PHP folder structure for AJAX calls and form actions

I am trying to develope good code organization habits and work exclusively with OOP in php but I can't seem to wrap my head around something.
Here is a simplified description of what I am working with:
I have all my class files in a folder '/resources/Classes'
I have all my html and javascript in '/public_html' & '/public_html/script respectively'
My question is concerning files that are the actions of forms or AJAX requests. For example 'formAction.php' and 'ajaxURL.php'. These files are not Classes and also do not contain any html or other such GUI.
I have been putting them in a folder 'resources/actions' but my gut tells me something about this is not fully OOP.
Is my usage of these files incorrect if I am trying for complete OOP? if so how can I approach this differently.
Here is an actual file from my project as a concrete example:
//file: getBalance.php
<?php
/**
* This script gets the balance of an account from the server
*/
if (!isset($Database)) {
$Database = require_once "../clear_finance_pkg.php";
}
/** #var User $User */
$User = $Database->getUserByID("1");//TODO: user should be set dynamically
$User->setAccounts($Database->getAccountsByUser($User));
if (isset($arg1)) {
$accountID = $arg1;
foreach ($User->getAccounts() as $Account) {
if ($Account->getId() == $accountID) {
$RbcChequing = RbcAccount::accountToRbcAccount($Account, "Chequing");
echo '$' . Money::toDollars($RbcChequing->getBalance());
break;
}
}
} else throw new Exception('Account ID is not set. Could not get balance');
It's difficult to say if your code is complete OOP, but i think it isn't. It looks like you are on the right track, because you are using classes, objects and methods. That's the basic of OOP. No longer large if/else statements and a lot of variables that doesn't make sense, but objects and methods without code-duplication.
I think your question in more related to the seperation of logic and view. First of all. In general it's ok to make a file for a function, but in a large application you will loose the overview. What you are doing know is combine view-related and logic-related things in one file, but actually that's not what you want. The ideal situation is full seperation of logic and view so you can create multiple seperate views based on the same logic.
My advice is to take a look at the MVC-pattern. Take a look at this link. It will help you to get a basic understanding of the MVC-pattern. Notice that you won't longer need to have a file for each function. And you have a seperation of your logic and view elements because you can use the same model in multiple views (although this is maybe not the best example).

How to simulate a request from view.

I'm new in cakephp and I'm just wondering, how to test models and controllers without using views?
I have to simulate saving data using models and controllers without using froms from views. I was thinking about to make an array with the needed values, but maybe there is a better way to do that?
you can mock your model functions using code like:
$model = $this->getMockForModel('MyModel', array('save'));
$model->expects($this->once())
->method('save')
->will($this->returnValue(true));
You can output variables at any time from controllers (or models) without getting to the views. Yes, it's not how you should do things with an MVC framework, but for testing, it's pretty easy to whack this below your database call in the model/controller:
<? echo '<pre>'; print_r($my_array); exit; ?>
The other thing you can do is at the top of your action function in the controller put:
$this->layout = '';
$this->render(false);
... which will bypass the layout and skip the view rendering, so you can output whatever you like within that function without using the view.
At the beginning of your action, you may use:
$this->autoRender = false;
This will allow you to access your action directly by going to it's path (e.g. CONTROLLER/ACTION). Before passing your data array to save() or saveAll(), I recommend double-checking it with Debugger::dump(), and follow that with die(). This will make the array containing the save data print on your screen so you can verify it looks proper and follows Cake's conventions. The die() will prevent it from actually saving the data.
If everything looks correct, remove the dump() and die() and test it out again.
The first response, from Ayo Akinyemi, should also work well if you are Unit Testing your application.

Can I pass data to the Codeigniter output class without displaying it?

I'm working on a way for users to be able to generate PDF copies of invoices and other tabular data. To do this, I've wrapped dompdf into a library that I can use with CI and created a method that will generate a PDF based on the return value of CI's output->get_output(). The wrapper is similar to this one on Github.
The problem is, I can't figure out a way to get the view (and HTML/CSS needed for the PDF) into CI's output class other than load->view(), which is going to write to the browser.
My only other choice would be to use curl to request the page, but that seems so silly to do since I can get it right from the output buffer. I just don't want the HTML sent to the browser, since I set headers telling the browser to expect a PDF.
To be clear, this is what I want to accomplish (in the order that I want to accomplish it):
Do everything I'd normally do to prepare the view for display
Load the view into the CI output class, but not display it
Pass the return value of output->get_output() to my dompdf library
Set the appropriate headers
Execute my dompdf method that will send the PDF to the browser
I don't see any way of doing step 2 based on the output class documentation.
Is it possible to get a view into the output class without displaying it? If so, how? I'm using CI 2.0.3.
Edit
The very helpful Anthony Sterling pointed out that I can just get what I want from the loader class by setting the third argument telling it to return a string rather than render the view to TRUE. E.g.:
$lotsaHtml = $this->load->view('fooview', $somearray, TRUE);
And that would be better in my particular instance since I don't need to load partials. However, this is still a valid and (I think) interesting question, it would also be handy to know if I could get the same from the OB, perhaps if I did have a bunch of partials. Those could be concatenated, but yuck.
It seems like I should be able to get the output class to not render anything (else, why does get_output() exist?) so I can do something else with everything it knows about. I just can't find a way to make that happen.
Edit 2
Some pseudo (but not far from reality) code illustrating what I hope to do, by showing what I did and then explaining what I actually wanted to do.
Let's say I have a public method genpdf($id) in a controller named invoice using a model named inv:
public function genpdf($invoiceId) {
$this->load->library('dompdflib');
$this->pagedata['invoice_data'] = $this->inv->getInvoice($invoiceId);
$html = $this->load->view('pdfgen', $this->pagedata, TRUE);
$this->dompdflib->sendPdf($html);
}
That is almost identical to code that I have that works right now. There, I ask the loader to parse and give me the results of the pdfgen view as a string, which I pass to the function in my dompdf wrapper that sets headers and sends the PDF to the browser.
It just seemed like this would be easy to do by just getting the output buffer itself (after setting headers correctly / etc).
Or do I just have to call the output class append_output() in succession with every partial I load?
Multiple methods loading a plethora of models need to work together to generate these (they're going in as an afterthought), so I was hoping to just collect it all and retrieve it directly from the output class. It could be that I just have to talk gradually to output->append_output() to make that happen.
...so - do I understand correctly - you want to get the whole final output (not just the view) as a string AND not display it to the user? Why dont you just overload the controllers _output() function?
class Your_controller extends CI_Controller
{
function stuff()
{
// do whatever - prep $data etc
$this->load->view('your_view', $data);
}
function _output($output)
{
// send $output to your library - get results blah blah
$result_pdf_file = $this->your_pdf_library_generator($output);
// Show something else to the user
echo "hi - I'm not what you expected - but here is your PDF";
echo $result_pdf_file; // or something like that
}
}
This means you can send ANYTHING you like to the output class - but nothing is displayed except what you want.
There are ways to improve this idea (i.e. hooks, variables to turn output on/off etc) - but the simplest would be to have this controller specifically for your pdf_generation command.
I don't see any way of doing step 2 based on the output class documentation. Is it possible to get a view into the output class without displaying it? If so, how? I'm using CI 2.0.3.
The controller _output() documentation is actually in the CI controller documentation, which is why it eluded you.

Categories