I want to save all but some excluded fields. I know that I can do it this way
$this->Blah->save($this->data,false,$fieldList)
Where $fieldList contains all the data fields of the table but these I don't want to get saved. I have some tables that have maaany data fields, and I don't want to write the whole list from scratch in every single controller action (yes, the fields that should not be saved differ from action to action). Additionally, it looky messy and confusing. Is cakePHP providing something ready-to-use for this case? If not, I guess, I'd have to implement it by myself by adding a $fieldList property to every controller and doing something like this (ugly-hacked-together-solution):
$tmp = $fieldList;
unset(array_search('fieldtoexclude', $tmp));
$this->Blah->save($this->data,false,$tmp);
Best Regards
function blacklist($blackList = array()) {
return array_diff(array_keys($this->schema()), $blackList);
}
shoud work
Take a look at:
http://www.dereuromark.de/2010/09/21/saving-model-data-and-security/
for details
If the field list changes from action to action, then you're looking for an automagic function that can read your mind. Cake doesn't provide that!
Somewhere you have to say which fields are to be excluded and doing so longhand in a clear way will make your code much more maintainable.
If it is only one controller, define the list as a class variable, or alternatively subclass the save action on the model.
Related
So, for weeks now I have been playing around with my own PHP MVC Framework, purely for learning purposes. I've got the basics done, and I think I know what goes where, etcetera. But I have so far not been able to figure out the following.
Say I have a music database, and I have a "music controller", a "music model" and obviously a "music view". What I want to end up doing is, being able to ofcourse insert a new artist, and/or a new track, also I would like to be able to edit them, and so on. My URL's look like this:
example.com/controller/mainfunction
In my "music controller" I have a method as follows:
public function addTrack()
{
if (isset($_POST["submit_add_track"]))
{
// call the model to add track to database etc..
}
}
So, if I were to have a form in my "music view" and submit that, I could add a new track to the database. I also have a method to add a new artist, and so on.
But here is what I am struggling with. How should I design this? Should I use the music controller to add tracks and artists? Like so:
example.com/music/add/artist
example.com/music/add/track
Or should I do it differently, maybe I should use the "music controller" just to display the artists and tracks, and insert stuff with another controller? Like so:
example.com/insert/artist
example.com/insert/track
The latter seems the way to go for me, but I am not sure if this would be good design.
I think maybe what I am asking is, would it be good to use an "insert" controller, and then a "track method" to display the "insert track" form in the view? Next to this, there will be a lot more pages, not only music. But I just cannot figure out the logic behind this on how to do this properly so I can easily extend.
As you can see, I'm lost, any pointers (big or small) would help me a lot. Thanks in advance!!
Edit:
So, just to see if I'm on the right track now. I have a music controller with the code pasted below. If in the controller the "addArtist" method get's called, it goes to the model, adds the artist to the database, returns something to the controller, and it's done. Now for the part where I still have troubles. How do I display the form to actually add an artist? I know it should be in the view. But how should I call it? Something like this?
example.com/music/insertArtist
This could work, but then I would have an "insertArtist" method, for just displaying the form, and an "addArtist" method, for actually inserting the new artist to the database each time the form is submit. I know I am missing something, but I cannot quite figure this out still. What I am thinking is, in the "addArtist" method, I include the "addArtist" view file to be displayed, which holds the form, and I put in some "if submit then add the artist". Does that make sense and if so, am I on the wrong track?
public function addArtist()
{
// $artist_model->addArtist();
}
public function editArtist($artistID)
{
// $artist_model->editArtist();
}
public function deleteArtist($artistID)
{
// $artist_model->deleteArtist();
}
public function addTrack()
{
// $artist_model->addTrack();
}
public function editTrack($trackID)
{
// $artist_model->addTrack();
}
public function deleteTrack($trackID)
{
// $artist_model->addTrack();
}
example.com/music/addArtist
example.com/music/addTrack
You must strictly follow the format of /controller/method
When I say method, I mean literally, method. In fact, in your base controller class, the way you should call a method is
/**
* Executes the requested action
*/
public function ExecuteMethod($method)
{
$this->{$method}();
}
where you would pass addArtist or addTrack to the method.
Basically, the /controller/ part just tells apache which actual controller class to construct, and the /method/ tells it which method in that controller to execute.
EDIT:
This is the best MVC tutorial I know of. I based my MVC knowledge largely on this. However, it is okay to variate the standard MVC structure a bit to suite your needs better, as long as you do not change the core values of MVC that make it so powerful.
EDIT 2: (reply to your edit)
So what you're asking it how to view the form.
Viewing the form and submitting the form are two entirely different actions, and should be treated as such. Additionally, EVERY controller action, MUST have it's own view.
An easy way to put it, every time a user needs ANY data (or action) from the server, you MUST have an action and a view for that.
What this means, is that you will have two separate methods in your controller, viewForm() and submitForm(). Now, what you showed in your controller class, where you had addArtist(), editArtist(), etc, is inefficient, as you are just aliasing them to their corresponding model functions, which sort of skips the entire point of MVC.
I can't really tell you exactly how to implement the controller, as I do not know exactly what your goal is. But let's assume that you have a page that lists all the Artists, and you have buttons next to each for all the actions (Btw, as I said in the comments, in an implementation like this, if you are displaying Artists and Tracks on different pages, they should really have separate controllers). In this implementation, you would have a controller -> view setup like this:
/Artists/view -> returns an html list of artists with buttons
/Artists/viewAddForm -> returns a blank form you would use for creating new artists
/Artists/submitAdd -> returns an html confirmation page
/Artists/viewEditForm -> returns a form for editing with prefilled values
/Artists/submitAdd -> returns an html confirmation page
/Artists/delete -> returns an html confirmation page
So a user is presented with a list of artists. They have 3 options.
They click add, fill out the form, submit it, and get taken to a confirmation page.
They click edit, edit the form, submit it, and get taken to a confirmation page.
They click delete, and they get taken directly to a confirmation page since there is no data to fill.
The structure for Tracks would be exactly the same.
The way I handled this in my framework was to try and support a RESTful interface. I have
example.com/artist
example.com/track
I then use the HTTP verbs to represent the intent (POST/PUT/DELETE/GET)
It doesn't always fit neatly, but it's one way of doing it.
In your case I would worry about putting too much code in a single file. If you put all the responsibility in an insert controller, that quickly becomes an unwieldy code file. In your case I would probably go with two controllers and then create the right methods on each.
This is a long running question that gets me every time I am developing.
I suppose it is not specific to CodeIgniter, but as I am using it, I will consider it in my example.
Firstly, which is better:
function add_entry($data_array)
{
//code to add entry
}
function edit_entry($data_array)
{
//code to update entry
}
OR
function save_changes($what,$data_array)
{
//if what == update update
//otherwise insert
}
Both produce the same action, but does it really matter which one you use?
Getting onto more complicated things.
I have a page where I need to get ONE entry from the database.
I also have a page where I need to get all the entries from the same database ordered by a user specified column.
My resultant method is a function similar to
function($data_array,$order_by='',$limit='')
{
//get where $data_array
//if order_by!='' add order by
//if limit !='' add limit
}
As I develop my application and realise new places where I need 'similar' database functionality I am what feels like hacking previous methods so they work with all my case scenarios. The methods end up containing lots of conditional statements, and getting quite complex with in some cases 4 or 5 input parameters.
Have I missed the point? I don't want duplicate code, and when for the most part the functions are very similar I feel like this 'hacking' methodology works best.
Could someone advise?
Finally my admin functionality is part of the same application in an admin controller. I have an admin model which contains specific methods for admin db interaction. I however use some model functionality from 'user' models.
FOr example if on an admin page I need to get details of a db entry I may load the user model to access this function. There is nothing wrong/insecure about this..? right?
In addition to that within my admin model itself I need to get data about a user database entry so I call my user model directly from my admin model. This is strictly OK, but why? If i need data and there is already a method in my user model which gets it.. it seems a little pointless to rewrite the code in the admin model BUT each time that function is called does it load the whole user model again?
Thanks a lot all.
In order, add edit in the model vs save. Personally I have a save built in MY_Model that chooses whether it is a save or an edit depending on the existence of a primary key in the data being passed, so obviously I prefer that method it means a lot less duplication of code since I can use the save for any table without having functions in the model at all.
As to the second question I think it depends on situation. I also have a number of functions that have a ton of conditionals on them depending on where they're used and for what. In some cases I'm finding this makes the legibility of the code a little rough. If you're running them all through if statements it also could be impacting performance. DRY is a concept, not a rule and like other design concepts there are times when they just don't make sense, it's like database normalization, it's my personal opinion it's VERY easy to over normalize a database and destroy performance and usability.
Finally, using user functions in the admin code. I don't see an issue here at all, the reverse probably isn't true, but rewriting a function just because it's an "admin" function, when it's identical to a user function is utterly pointless. So you're correct there, it's a waste of time and space.
TL;DR - How do I massively assign private fields in Yii?
Any Yii experts on StackOverflow? The YiiFramework forums didn't really help me out.
I've got a private field hired in my CActiveRecord model that is dependent on another relation jobCount. Basically, if there is at least one valid job (stored in another table) associated with that member, they are consider hired.
Conventionally, I would set hired in the afterFind method, but that would mean loading the relation every time. For the sake of saving database queries, I would only like to load the relation if hired is needed. So I set hired to private, and can load the relation and set it once getHired() is called.
So far so good...
The problem arises once I incorporate the hired field in my CGridView. I'd like to be able to use the column filters, with a simple dropdown filtering on Yes or No. Upon filling out your filters, CGridView passes back a GET request, which you would set to a cleared model using massive assignment...
$model->attributes = $_GET['ModelName'];
Obviously I would like hired to get set as well, despite it being a private field. (I handle the searching for CGridView, don't worry about that.) I've made it a safe field in my model, but it doesn't get set.
setHired() function doesn't get called
setAttribute() function doesn't get called
setAttributes() function doesn't get called
What's the correct way to do this? Clearly, I could just add an extra line in my controller action...
if (isset($_GET['ModelName']['hired']))
$model->setHired($_GET['ModelName']['hired']);
...but I would really rather learn how to allow private fields to be massively assigned.
I realize that this is rather convoluted. If you see some way that I could streamline this hired bit, I'd appreciate that. Still, I would like to learn if there's a way to do this.
I suppose, you need just to add your attribute to the list of attributes.
public function attributeNames()
{
$names = parent::attributeNames();
$names[] = 'hired';
return $names;
}
I am using CakePHP and want to pass the author's name (stored in a session) to an article that is being saved to the database. Is a hidden field the only way to do this or is there a better way?
If you already have it in the session, I would not spend the extra code/time to add it to a hidden field. I would update the method to add the session variable to the $this->data so it will add it when you save the record. So the method in the controller would look something like:
function add() {
if($this->data) {
$this->data['Article']['author'] = $this->Session->read('User.name');
$this->Article->create();
if ($this->Article->save($this->data)) {
...
}
This way you are not dealing with all the extra work and you can still achieve the results you are looking for.
There are other ways, but a hidden field is as convenient as any other and probably the most transparent (i.e. most detectable by other developers who may pick up the code later). You could also insert the value into the $this->data structure before save and be sure that your model knows what to do with it.
I've recently started to rewrite a project I did a few years ago using CakePHP. I'm trying to do everything 'right' this time, so maybe someone get give me a a pointer on doing to the following:
I'm showing a simple table from a table using Model->find('all') in the View. There are two boolean fields in this table, that together make up something I need to show to a user. So: 0x0 = 'A', 1x0 = 'B', 0x1 = 'C', 1x1 = 'D'. Where should I put this logic? I've been thinking about the following methods:
The view
A View helper
The controller
Something in the Model so that Model->find('all') outputs this value (is that even possible?)
This task might seem trivial, but I think it might learn me getting this project organized and maintainable from the start.
Thanks!
Well, it depends on the type of logic for making up final table (is it presentation or business?).
Imagine you add new type of UI, for example command line interface. How would you show your table there? The data passed to View has to be same for both HTML and console presentations. So the logic which is responsible for preparing that data - is business logic and it should be placed in Model. The logic responsible for displaying the data should be placed in View (maybe in view helper if it's used more than once).
And never place this kind of logic in Controller.
If it's something you're going to use all over the place I would put it in the model. You can either put a method on the model that gives that value back or loop over all the rows you've retrieved in an afterFind callback and set it as a proper field.
I put this kind of logic in the view if it is something that is going to determine rendering style. In that way, the designer has maximum access and can style accordingly.
On the other hand, if the two columns only exist for convenience in datamodelling, put it in the model. The designer shouldn't even be aware of other possibilities!
In the controller! The methods from the model comes in the controller. The view is just for output( like HTML UI programming.)