I'm developing an application in Cake that heavily interacts with the filesystem. The basic idea is to monitor and index a folder and it's subfolders and files and save meta data for each file in the database. It's working fine so far, but now I got kind of a problem in understanding the MVC mechanics.
I got this heavy FilesController with a lot of functions checking if a file is up-to-date or if it has moved, updating the database entries and so on... Now I want to call some of this actions from a shell/cronjob and I want to call them in the browser too.
I've heard a lot of people complaining about importing controllers in shells and I think I got the idea why this is a bad idea. But now I want to use the same code that interacts with the FileModel directly in a shell and in a controller. Where to put it? What is best practice in this situation? In a component, loading the model? In the controller, importing it in the shell?
Thanks for your help in advance <3
I got this heavy FilesController with a lot of functions checking if a
file is up-to-date or if it has moved,
Wrong place, heavy controllers are definitely wrong. You want fat models, skinny controllers. If there is logic that extracts meta data I probably would put it into app/Utility/FileMetaData or app/Lib/FileMetaData.php and make it a new class. Depending on what it does you might extend the core class Folder or File.
The processing logic for the meta data and reading the folder should go into a model. The model can be used from the shell like in a controller by using the $uses property with an array of models.
To instanitate that class I would use a helper method (I don't mean a view helper by this!) in the model like getMetaDataReader() that returns the instance. The reason for this is to be able to mock the result of that method call in an unit test. Also, if you ever change the class or constructor args you'll have to change only a single place.
Controllers are clearly wrong in shells. There is no request to deal with. Sure technically you can do the stunt and instantiate them there but it is just wrong and doesn't make any sense. If you think you have to or "need" to do so, something is wrong with your application architecture.
You might want to take a look at my FileStorage plugin as well. You can implement an event listener there and when storing a file have the listener automatically process the the meta data.
Related
I'm using laravel 4 and trying to write a test for controller with a mock up of the business logic which pass some fake data.
Then I encounter errors in rendering view due to missing of variables.
First of all, I didn't expect that the view will be rendered, so I didn't care to pass the exact data structure that is needed.
Should I pass the correct data structure to the view or is there a way to stop view from rendering in the test? Which is the better practice and why?
Moreover, after attempting to imitate data structure passing by business logic, there is still error cause by permission of user.
The result temp file of the view has been generated by web user (www-data) and I don't have permission to modify it.
I know, I can just chmod 777, but this is clearly not a good practice, if I have to do that for every time I want to run the test.
Thanks in advance
I suppose your controllers are not messed up. Every logic should be injected through constructor or resolved from the IOC. Mock external classes and bind mocked instances on your test setUp. Facades already have support for Mockery. You can use XXX::shouldReceive().
Also, I suggest you to take a look at Illuminate\Foundation\Testing\TestCase. It has helper methods like assertViewHas, assertSessionHas, assertRedirectedTo etc. With these, testing your controller will be really easy.
After all, best thing to do for you would be reading "Laravel Testing Decoded" by Jeffrey Way. It'a short and amazing book. https://leanpub.com/laravel-testing-decoded
i had a similar problem with the view breaking up while testing. in my case it was a foreach loop, that didn't get the right structure, so i changed my mock to return an empty array, this made the test pass. so this is a possible solution for you i think, though it doesn't mock or just forget rendering the view itself.
I'm trying to get to grips with the MVC structure and trying to decide where my files should go.
I have a php script which reads an image from a non-web accessible location and outputs it.
Does it belong in the Controller or the View?
Likewise, should a script that loads a smarty template (from the view) and sets values and output it be within the Controller or the View?
Thanks for your help!
I Second. Model because controllers get fat so fast.
It should be a model as only Models should be allowed to know where data can be found and how they should be access (filesystem in your case). Also data conversion is best done in a model.
Dare to output compressed JPGs instead 10MB+ BMP-files on the fly? If your server can manage it your $ImageModel->outputAsJPEG() could be called in the controller, skipping viewscripts alltogether or delegated to the viewscripts/smarty functions.
Both your examples sound like Model logic to me, but the second example is a little more fuzzy. A template is a View but setting values is probably Model logic if it's non-trivial.
Your View should invoke a Model to get stuff it needs, and the Controller should be deciding which View to display and instantiating the right Model to hand off to the View.
If you can treat your proxy image script as a Model then just invoke it in the View.
Only Model in this situation.
Ok, it's my fault. I've never ever learned programming at a school and that's why i'm always ending up in a spaghetti code. I've always curious about different patterns and tried to understand them at least in a basic level.
MVC is my worst fear and i think i'll be never able to use it's advantages because of i don't understand it's fundamentals.
My actual question/problem looks like:
The front controller calls a 'Core' class which is doing some initialization then it calls the actual controller with the correct action/parameters. The controllers always extending the 'Core' class so i can acces it's variables, etc. They're working nicely together but here comes my real problem.
Some kind of methods (getting a database entry in most of the cases) are required in different cases. (e.g. a product needs it's manufacturer)
In this scenario i have two (bad) choices:
Inject the required method into the 'Core' class so it's getting bloated over time
Inject the required method into the actually called controller so i will end up a redundant codebase
I see a lot of possible problems in my approach:
Controllers are always extending 'Core' class
'Core' controller holds the database object so without it i cannot access my Db
Database functions (e.g. getting a product) are in the controllers but i cannot access them because they're always calling 'Core' first (extending problem again)
Please tell me:
Where is the biggest problem in my approach and where can i correct it?
Note:
Please don't treat this as a general question, i think this is an answerable thing. If you need some clarification, please ask for it and i'll try to lighten up things.
Thanks for your precious time, fabrik
Your data is represented to your Controller and View through the Model. The Model may be supported by a Repository but depending on the overhead you might want to provide your database access in your Model. The data architecture should be similar to this:
(Repository<===>)Model<===>Controller--->View
Your biggest problem is having the "Core" class, get rid of it asap.
By the way, the FrontController is not the only way to do things MVC things either.
Your second problem is that controller deals with database, it shouldn't. I suggest you use some abstract data layer, which you use only in your models. And controller should deal only with models, it shouldn't care how models get their data persisted and fetched.
Look into using a DI framework to automatically inject an instance of a repository into your controllers (or even better, a proxy class). Try to keep business logic outside of your controllers, but rather, refector it out into a helper or proxy class.
I tend to split my logic up into views => controllers (just for interaction between business later and view) => business logic => models (DTOs) => low-level data access at a minimum.
Also, if you need common helper functionality in your views, perhaps create several extensions to help.
I know normally the data is passed thru to the view with the controller. however, currently in my view I load my model ($this->load->model('Db_model');) so i can use it in a loop to retrieve a users profile picture path from a array of IDs that is passed from controller. Will loading the db model in the view to accomplish this make my site more vulnerable or bad form? To me it seems to be outside of MVC concept but its working atm. thanks
I agree, but it has everything to do with scale. If you are designing a tiny app MVC does not really matter because it is easy to oversee the whole application. However once an application starts to grow, or if you are building a bigger application MVC separation becomes more important.
For instance if you use models in your views, this implies the design team has to know about models. It may also hamper porting the views later on to another framework or swapping templates.
Well if you do something outside of MVC it doesn't mean that it will stop working the very same second. MVC is just a design pattern which should help you design and maintain your site. The basic principle is that model should communicate only with controller and view also only with controller so your idea of calling model directly from the view is not MVC way of doing things.
If you need some additional data from the model why don't fetch it in controller and pass it as another parameter to the view so it can easily use it? It will be the same amount of code probably and your code will be much cleaner. Keeping your code clean may not seem like such a big deal right know when you remember where everything is stored but in few months when you forget some of this stuff you may end up with a headache if you mess your app too much.
Look into Libraries. You should consider creating a library class helper for displaying the profile picture. You can then have the library call the model. Then, in your view, you simply do:
<?php $this->profile_helper->display_picture(); ?>
where profile_helper is your library class and display_picture() is your class function to display the users profile.
straight to the point :
I am using Kohana, and I am looking at another script written in plain PHP. In the script, I have a class ShoppingCart. If I am to convert the script to Kohana, where am I to put the class, its methods and its properties?
Is it in my existing default controller? Or should I put it in a separate controller? Or as noobie as it may sound, will I put it in the model?
That depends on the specifics of the class I suppose. To be honest I don't know anything about Kohana, but there's probably a place for "vendor files" somewhere. Maybe it's best to place it there and write wrapper functions for it in your controller. If the class already integrates well with Kohana you may instead choose to use it as a controller or model directly. Or you might want to take the time to rewrite it to make it work as a controller...
Only you can evaluate the best place for it, there's no hard and fast rule here.
Kohana has a folder for 3rd party libraries. The main one is under system/vendor, you can put it in you application/ as well.
Many PHP class loaders require details like your filename should be the same as the class name (at least that's what I read in the Kohana documentation) if you want the classes to be automatically loaded.
If you need to use 3rd party code in your app it's recommended that you create a folder in your app / module folder called 'vendor' and place all of that code there.
You can then include the files by calling:
include kohana::find_file('vendor', 'filename');
If needs be you can also create a wrapper for the external library, a good example of this is the email helper which uses the 3rd party Swift email library.
If you're porting your own class to kohana then you need to work out what the class will be doing and categorise it accordingly.
If the class will be fetching items from some kind of database then you should make it a model. Libraries are usually sets of code that you want reuse across controllers / models, such as authentication, calendar generation etc. Controllers are used for passing data from models to your views / libraries.
See the docs for more info
As per kohana convention, you should place custom classes in application/libraries folder. However for this, you need to know how to get the class to work after putting it there. If you can't figure that out, you can do anything like putting it in your controller or making another controller of it, etc.