I come from the procedural PHP and am learning OOP with Laravel. What I learned so far is very interesting and will ease my developer's life (it's not my job btw).
So, for all my websites, I am using a slug property for all articles, categories, and so on.
I started to use the "str_slug" provided by Laravel which seems to do the job at 99%. The issue I get is when I have such title (in french): "J'ai mangé une pomme", the slug string I get is: "jai-mange-une-pomme" which, in french, is not correct. I would like "j-ai-mange-une-pomme".
It's not really an issue. I can do:
$slug = str_replace('\'','_',$input['name']);
$slug = str_slug($slug, '-');
It suits me well but I wonder how to use anytime I want to use it. I don't want to write it again and again and again.
In procedural, it's easy, I would write a function, such as thePerfectSlug(){} in a helpers.php file (still an example) and will use an include at the top of my index.php. That would do the job.
But in OOP and especially in Laravel (5.1), how can I do that?
Thanks
You still can achieve it with normal function. Laravel uses his own function which are stored in helpers.php file. You can make your own helpers.php file and add it to your main composer.json file at autoload.files.
If you would like to do it in OOP way, create a trait like App\Traits\Sluggify with your method and use it in any class that needs it.
Related
please can anyone help me understand what a macro is in Laravel Macroable trait, reading this documentation https://laravel.com/api/5.4/Illuminate/Support/Traits/Macroable.html only tells me how to use but why do I use it, what is it meant for.
It is for adding functionality to a class dynamically at run time.
use Illuminate\Support\Collection;
Collection::macro('someMethod', function ($arg1 = 1, $arg2 = 1) {
return $this->count() + $arg1 + $arg2;
});
$coll = new Collection([1, 2, 3]);
echo $coll->someMethod(1, 2);
// 6 = 3 + (1 + 2)
echo $coll->someMethod();
// 5 = 3 + (1 + 1)
We have 'macroed' some functionality to the Collection class under the name someMethod. We can now call this method on the Collection class and use its functionality.
We just added a method to the class that didn't exist before without having to touch any source files.
For more detail of what is going on, please check out my article on Macros in Laravel:
asklagbox - blog - Laravel Macros
It allows you to add new functions. One call to ::macro adds one new function. This can be done on those of the internal framework classes which are Macroable.
This action of adding the function to the class is done at run time. Note there was/is an already existing perfectly good name for this action, which isn't the word "macro", which I'll explain at the end of this post.
Q. Why would you do this?
A. If you find yourself juggling with these internal classes, like
request & response, adding a function to them might make your code more
readable.
But as always there is a complexity cost in any
abstraction, so only do it if you feel pain.
This article contains a list of the classes you can add functions to using the static call "::macro"
Try not to swallow the word macro though, if you read that article - if you're like me it will give you big indigestion.
So, let's now add one extra function to an internal framework class. Here is the example I have just implemented:
RedirectResponse::macro('withoutQuery', function() {
return redirect()->to(explode('?', url()->previous())[0]);
});
This enables me in a controller to do this:
redirect()->back()->withoutQuery();
(You can just do back() but I added redirect() to make it clear).
This example is to redirect back and where the previous route was something like:
http://myapp.com/home?something=something-else
this function removes the part after '?', to redirect to simply:
http://myapp.com/home
I did not have to code it this way. Indeed another other way to achieve this is for me to put the following function in the base class which all controllers inherit from (App\Http\Controllers\Controller).
public function redirectBackWithoutQuery()
{
return redirect()->to(explode('?',url()->previous())[0]);
}
That means I can in any controller do this:
return $this->redirectBackWithoutQuery();
So in this case the "macro" lets you pretend that your new function is part of an internal framework class, in this case the Illuminate/RedirectResponse class.
Personally I like you found it hard to grasp "laravel macros". I thought that because of the name they were something mysterious.
The first point is you may not need them often.
The second point is the choice of the name ::macro to mean "add a function to a class"
What is a real macro?
A true macro is a concept unique to Lisp. A macro is like a function but it builds and returns actual code which is then executed. It is possible to write a function in other languages which returns a string which you then execute as if it was code, and that would be pretty much the same thing. However if you think about it you have all of the syntax to deal with when you do that. Lisp code is actually structured in lists. A comparison might be imagine if javascript was all written as actual json. Then you could write javascript, which was json, which returned json, which the macro would then just execute. But lisp is a lot simpler than json in terms of its syntax so it is a lot easier than what you just imagined. So, a true lisp macro is one of the most beautiful and amazing things you can encounter.
So why are these add-a-function things in laravel called macros?
That's unknown to me I'm afraid, you'd have to ask the author, but I asked myself what they really do and is there already a name for that.
Monkey Patches
TL;DR laravel's ::macro could more accurately be described as monkey patch
So if using laravel ::macro calls, I personally decided to create a MonkeyPatchServiceProvider and put them all there, to reduce unnecessary confusion for myself.
I realise the name might sound a bit derogatory, but that's not intended at all.
It's simply because there's already a name for this, and we have so much terminology to deal with why not use an existing name.
I am using codeigniter for a project that is used by a variety of companies.
The default version of our software is up and running and works fine - however some of our customers want slightly different view files for their instance of the system.
Ideally what I would like to do is set a variable (for example VIEW_SUFFIX) and whenever a view file is loaded it would first check if there was a suffix version available if there was use that instead.
For example if the system had a standard view file called 'my_view.php' but one client had a VIEW_SUFFIX of 'client_1' - whenever I called $this->load->view('my_view') if the VIEW_SUFFIX was set it would first check if my_view_client_1 existed (and if it did use that) or if not use the default my_view.php.
I hope that my question is clear enough... If anyone has done this before or can think of a way to do it I would really appreciate it.
EDIT:
Ideally I would like a solution that works without me changing every place that I am calling the view files. Firstly because there are a few files that may want different client versions and also because the view files are called from a lot of controllers
I had a similar requirement for which I created a helper function. Among other things, this function can check for a suffix before loading the specified view file. This function can check for the suffix and check if the file exists before loading it.
Unfortunately, the file checking logic would be a bit brittle. As an alternative, you can implement a MY_Loader class that will override the basic CI_Loader class.
Something like this in your application/core/MY_Loader.php:
class MY_Loader extends CI_Loader {
protected function _ci_load($_ci_data)
{
// Copy paste code from CI with your modifications to check prefix.
}
}
Could you not do this
// some method of creating $client
// probably created at login
$_SESSION['client'] = 'client_1';
$client = (isset($_SESSION['client'])) ? $_SESSION['client'] : '';
$this->load->view("your_view{$client}", $data);
I want to override the guestbook functionality. To be exact, I want to override the action_form_save_entry() function on [mysite]/concrete5/core/controllers/blocks/guestbook.php
I've tried to override it these ways:
[mysite]/controllers/blocks/guestbook.php
[mysite]/core/controllers/blocks/guestbook.php
noe of them works. I can't find any way how to override that file. The documentation here and here doesn't show how to override that /core/ directory. Their forum never helps. Google result also just get misled with the 'core' keyword. All the result just take the 'core' meaning as just what's exist on the /concrete5/ directory, not the exact true /concrete5/core
Looks like that /concrete5/core/ directory appear only on the newer version. CMIIW.
Btw, maybe I should also tell you what I want to do with that function. Probably you have another workaround for this instead of simply overriding it. I want to add SMS notification functionality to it. So whenever someone submit a new comment, an SMS would be sent to the admin of a particular page.
Yes, the /concrete/core directory structure is new to 5.6. Tutorials and documentation on c5 can be ... lacking ... but in this case it's just a matter of them being behind a bit.
The "real" guestbook controller is at /concrete/blocks/guestbook/controller.php. You'll notice that it's just a shell of a class:
class GuestbookBlockController extends Concrete5_Controller_Block_Guestbook {}
The file that you referenced defines Concrete5_Controller_Block_Guestbook.
So, the solution is to override the real controller, not whatever it extends (ie, the file that you were looking at). Thinking in this way, it should be clearer that you need to create a file at /blocks/guestbook/controller.php. In fact, just copy the controller.php that I referenced above because you need to keep the (sometimes multiple) classes. Then, you can override the particular function. (Don't forget to call parent::action_save_form_entry()).
I know there are a couple of other topics about this subject, but non of them seems to fit my needs.
What I have
example.com/log/
LogsController.php
I have LogsController instead of LogController (plural) because CakePHP wants you to have controllers in plural.
But as you might know/notice, example.com/log/ will never use LogsController because of the missing 's' in the url.
Now, I want to have /log/* being redirected to /logs/*. Works perfectly fine with the following code:
Router::connect ('/log/*', array('controller'=>'logs'));
But, when I try to access example.com/log/actions/foo/bar it doesn't seem to work. So after some Googeling I found this:
Router::connect ('/log/:action/*', array('controller'=>'logs'));
Works great. But now when I'm trying to access example.com/log/ again, it says
Error: LogController could not be found.
Question
So my question is, how do I set up an alias for my url so that /log/ will use LogsController instead of trying to use LogController.
I have a few more Controllers where I'd like to change this, like flight => FlightsController, profile => ProfilesController.
Have a look at this question. It is about the same subject, but slightly different. It might help you in some way.
Ok, with the help of some other people on IRC and stuff like that. I found out the following.
A combination of
Router::connect('/flight/:action/*', array('controller'=>'flights'));
Router::connect('/flight/*', array('controller'=>'flights'));
does the trick. I tried this before, but in a other order, like so:
Router::connect('/flight/*', array('controller'=>'flights'));
Router::connect('/flight/:action/*', array('controller'=>'flights'));
which doesn't work.
So the first 2 lines of code in this post solved it for me. Another guy told me that the solution of Arun Jain isn't a proper solution, because it changes the nameconventions in the core as well. Which will cause problems with the FormsHelper and classes like that. So I think I will prefer the code in this post since this is just an alias instead of a core changing piece of script. Thanks for the help anyway.
To do this with routing the correct approach is as follows.
Router::connect('/flight', array('controller'=>'flights','action'=>'index'));
Router::connect('/flight/:action/*', array('controller'=>'flights'));
This tells the router that when an action is found in the URL to use it, but it no params are found then to default is to use the index action.
I have a slightly different take on all of this. The plural is more often the more accurate way to go with things but in occassions when the plural is just plain wrong, i add an exception into the Inflector class (/lib/Cake/Utility/Inflector).
In your example I would add log to this list of uninflected words. This means that system wide cake will not append the 's'. You'll have your LogController your views would sit in the Log view folder etc...
EDIT
I've come across a much neater way to do this from within app/config/bootstrap.php
Inflector::rules(
'plural',
array(
'uninflected' => array('log')
)
);
This would add log to the uninflected list without having to alter the files within the Core to allow easier version updating.
You can simply do it using following:
class LogsController extends AppController
{
public $name = 'Log';
..... YOUR REMAINING CODE ......
}
Your router connnect code will remain same. Kindly ask if it not worked for you.
I'm using GWT to dynamically load html snippets from php script. I define the snippet i want the php script to return in the url (test.php?snippet=1). Now in GWT i have a function "getSnippet(int snippet id)" that uses a RequestBuilder to retrieve the snippet. It works perfectly fine, but it bothers me that i have to create a new RequestBuilder everytime getSnippet gets called. I'd rather have one ReqestBuilder and just change the url when getSnippet is called...
Is there a way to do this ?
Thank you !
In looking at the source code, I can't see a good reason why they are doing this. I would like to think that the GWT developers decided to leave out the setUrl method for a reason and included it in the constructor instead.
If you really want to do it, one way around this would be to extend the class and add a setUrl(String url) method. Modify all your current uses of RequestBuilder to use your newly extended class and see if anything breaks.